diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 17:21:03 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 16:25:15 +0000 |
commit | c551f43206405019121bd2b2c93714319a0a3300 (patch) | |
tree | 1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/base/task/promise | |
parent | 7961cea6d1041e3e454dae6a1da660b453efd238 (diff) |
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base/task/promise')
17 files changed, 781 insertions, 323 deletions
diff --git a/chromium/base/task/promise/abstract_promise.cc b/chromium/base/task/promise/abstract_promise.cc index f48ba8517e2..315cfd30bfc 100644 --- a/chromium/base/task/promise/abstract_promise.cc +++ b/chromium/base/task/promise/abstract_promise.cc @@ -8,6 +8,7 @@ #include "base/lazy_instance.h" #include "base/sequenced_task_runner.h" #include "base/task/promise/dependent_list.h" +#include "base/task/promise/post_task_executor.h" #include "base/threading/sequenced_task_runner_handle.h" namespace base { @@ -65,6 +66,10 @@ AbstractPromise::~AbstractPromise() { OnCanceled(); } +void AbstractPromise::EmplaceResolvedVoid() { + emplace(Resolved<void>()); +} + bool AbstractPromise::IsCanceled() const { if (dependents_.IsCanceled()) return true; @@ -290,7 +295,7 @@ AbstractPromise* AbstractPromise::GetCurriedPromise() { const PromiseExecutor* AbstractPromise::GetExecutor() const { if (!value_.ContainsPromiseExecutor()) return nullptr; - return value_.Get<internal::PromiseExecutor>(); + return value_.Get<PromiseExecutor>(); } PromiseExecutor::PrerequisitePolicy AbstractPromise::GetPrerequisitePolicy() { @@ -509,7 +514,7 @@ void AbstractPromise::OnRejectMakeDependantsUseCurriedPrerequisite( void AbstractPromise::DispatchPromise() { if (task_runner_) { - task_runner_->PostPromiseInternal(this, TimeDelta()); + task_runner_->PostPromiseInternal(WrappedPromise(this), TimeDelta()); } else { Execute(); } @@ -670,14 +675,66 @@ void AbstractPromise::AdjacencyList::Clear() { prerequisite_list_.clear(); } else { // If there's multiple prerequisites we can't do that because the - // DependentList::Nodes may still be in use by some of them. Instead we - // release our prerequisite references and rely on refcounting to release - // the owning AbstractPromise. + // DependentList::Nodes may still be in use by some of them. + // Instead we release our prerequisite references and rely on refcounting to + // release the owning AbstractPromise. for (DependentList::Node& node : prerequisite_list_) { node.ClearPrerequisite(); } } } +BasePromise::BasePromise() = default; + +BasePromise::BasePromise( + scoped_refptr<internal::AbstractPromise> abstract_promise) + : abstract_promise_(std::move(abstract_promise)) {} + +BasePromise::BasePromise(const BasePromise& other) = default; +BasePromise::BasePromise(BasePromise&& other) = default; + +BasePromise& BasePromise::operator=(const BasePromise& other) = default; +BasePromise& BasePromise::operator=(BasePromise&& other) = default; + +BasePromise::~BasePromise() = default; + } // namespace internal + +WrappedPromise::WrappedPromise() = default; + +WrappedPromise::WrappedPromise(scoped_refptr<internal::AbstractPromise> promise) + : promise_(std::move(promise)) {} + +WrappedPromise::WrappedPromise(internal::PassedPromise&& passed_promise) + : promise_(passed_promise.Release(), subtle::kAdoptRefTag) { + DCHECK(promise_); +} + +WrappedPromise::WrappedPromise(const Location& from_here, OnceClosure task) + : WrappedPromise(internal::AbstractPromise::CreateNoPrerequisitePromise( + from_here, + RejectPolicy::kMustCatchRejection, + internal::DependentList::ConstructUnresolved(), + internal::PromiseExecutor::Data( + base::in_place_type_t<internal::PostTaskExecutor<void>>(), + std::move(task)))) {} + +WrappedPromise::WrappedPromise(const WrappedPromise& other) = default; +WrappedPromise::WrappedPromise(WrappedPromise&& other) = default; + +WrappedPromise& WrappedPromise::operator=(const WrappedPromise& other) = + default; +WrappedPromise& WrappedPromise::operator=(WrappedPromise&& other) = default; + +WrappedPromise::~WrappedPromise() = default; + +void WrappedPromise::Execute() { + DCHECK(promise_); + promise_->Execute(); +} + +void WrappedPromise::Clear() { + promise_ = nullptr; +} + } // namespace base diff --git a/chromium/base/task/promise/abstract_promise.h b/chromium/base/task/promise/abstract_promise.h index 9f67c688bd4..c16132c187c 100644 --- a/chromium/base/task/promise/abstract_promise.h +++ b/chromium/base/task/promise/abstract_promise.h @@ -23,6 +23,9 @@ class TaskRunner; template <typename ResolveType, typename RejectType> class ManualPromiseResolver; +template <typename ResolveType, typename RejectType> +class Promise; + // AbstractPromise Memory Management. // // Consider a chain of promises: P1, P2 & P3 @@ -265,13 +268,71 @@ enum class RejectPolicy { kCatchNotRequired, }; +class WrappedPromise; + namespace internal { template <typename T, typename... Args> class PromiseCallbackHelper; -class PromiseHolder; +class AbstractPromise; class AbstractPromiseTest; +class BasePromise; + +// A binary size optimization to reduce the overhead of passing a scoped_refptr +// to Promise<> returned by PostTask. There are many thousands of PostTasks so +// even a single extra instruction (such as the scoped_refptr move constructor +// clearing the pointer) adds up. This is why we're not constructing a Promise<> +// with a scoped_refptr. +// +// The constructor calls AddRef, it's up to the owner of this object to either +// call Clear (which calls Release) or AbstractPromise in order to pass +// ownership onto a WrappedPromise. +class BASE_EXPORT PassedPromise { + public: + explicit inline PassedPromise(const scoped_refptr<AbstractPromise>& promise); + + PassedPromise() : promise_(nullptr) {} + + PassedPromise(const PassedPromise&) = delete; + PassedPromise& operator=(const PassedPromise&) = delete; + +#if DCHECK_IS_ON() + PassedPromise(PassedPromise&& other) noexcept : promise_(other.promise_) { + DCHECK(promise_); + other.promise_ = nullptr; + } + + PassedPromise& operator=(PassedPromise&& other) noexcept { + DCHECK(!promise_); + promise_ = other.promise_; + DCHECK(promise_); + other.promise_ = nullptr; + return *this; + } + + ~PassedPromise() { + DCHECK(!promise_) << "The PassedPromise must be Cleared or passed onto a " + "Wrapped Promise"; + } +#else + PassedPromise(PassedPromise&&) noexcept = default; + PassedPromise& operator=(PassedPromise&&) noexcept = default; +#endif + + AbstractPromise* Release() { + AbstractPromise* promise = promise_; +#if DCHECK_IS_ON() + promise_ = nullptr; +#endif + return promise; + } + + AbstractPromise* get() const { return promise_; } + + private: + AbstractPromise* promise_; +}; // Internal promise representation, maintains a graph of dependencies and posts // promises as they become ready. In debug builds various sanity checks are @@ -308,12 +369,11 @@ class BASE_EXPORT AbstractPromise RejectPolicy reject_policy, ConstructType tag, PromiseExecutor::Data&& executor_data) noexcept { - scoped_refptr<AbstractPromise> promise = - subtle::AdoptRefIfNeeded(new internal::AbstractPromise( - nullptr, from_here, nullptr, reject_policy, - tag, std::move(executor_data)), - AbstractPromise::kRefCountPreference); - return promise; + return subtle::AdoptRefIfNeeded( + new internal::AbstractPromise(nullptr, from_here, nullptr, + reject_policy, tag, + std::move(executor_data)), + AbstractPromise::kRefCountPreference); } AbstractPromise(const AbstractPromise&) = delete; @@ -347,7 +407,9 @@ class BASE_EXPORT AbstractPromise public: PromiseValue& value() { return value_; } +#if DCHECK_IS_ON() ~ValueHandle() { value_.reset(); } +#endif private: friend class AbstractPromise; @@ -357,6 +419,8 @@ class BASE_EXPORT AbstractPromise PromiseValue& value_; }; + // Used for promise results that require move semantics. E.g. a promise chain + // involving a std::unique_ptr<>. ValueHandle TakeValue() { return ValueHandle(value_); } // Returns nullptr if there isn't a curried promise. @@ -380,6 +444,10 @@ class BASE_EXPORT AbstractPromise "Use scoped_refptr<AbstractPromise> instead"); } + // An out-of line emplace(Resolved<void>()); Useful for reducing binary + // bloat in executor templates. + void EmplaceResolvedVoid(); + // This is separate from AbstractPromise to reduce the memory footprint of // regular PostTask without promise chains. class BASE_EXPORT AdjacencyList { @@ -457,9 +525,14 @@ class BASE_EXPORT AbstractPromise void IgnoreUncaughtCatchForTesting(); - private: - friend class AbstractPromiseTest; + // Signals that this promise was cancelled. If executor hasn't run yet, this + // will prevent it from running and cancels any dependent promises unless they + // have PrerequisitePolicy::kAny, in which case they will only be canceled if + // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or + // OnRejected() has already run, this does nothing. + void OnCanceled(); + private: friend base::RefCountedThreadSafe<AbstractPromise>; friend class AbstractPromiseTest; @@ -470,8 +543,6 @@ class BASE_EXPORT AbstractPromise template <typename T, typename... Args> friend class PromiseCallbackHelper; - friend class PromiseHolder; - template <typename ConstructType> AbstractPromise(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, @@ -520,13 +591,6 @@ class BASE_EXPORT AbstractPromise // have been canceled, in which case null is returned. AbstractPromise* FindCurriedAncestor(); - // Signals that this promise was cancelled. If executor hasn't run yet, this - // will prevent it from running and cancels any dependent promises unless they - // have PrerequisitePolicy::kAny, in which case they will only be canceled if - // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or - // OnRejected() has already run, this does nothing. - void OnCanceled(); - // Signals that |value_| now contains a resolve value. Dependent promises may // scheduled for execution. void OnResolved(); @@ -714,7 +778,115 @@ class BASE_EXPORT AbstractPromise std::unique_ptr<AdjacencyList> prerequisites_; }; +PassedPromise::PassedPromise(const scoped_refptr<AbstractPromise>& promise) + : promise_(promise.get()) { + promise_->AddRef(); +} + +// Non-templatized base class of the Promise<> template. This is a binary size +// optimization, letting us use an out of line destructor in the template +// instead of the more complex scoped_refptr<> destructor. +class BASE_EXPORT BasePromise { + public: + BasePromise(); + + BasePromise(const BasePromise& other); + BasePromise(BasePromise&& other) noexcept; + + BasePromise& operator=(const BasePromise& other); + BasePromise& operator=(BasePromise&& other) noexcept; + + // We want an out of line destructor to reduce binary size. + ~BasePromise(); + + // Returns true if the promise is not null. + operator bool() const { return abstract_promise_.get(); } + + protected: + struct InlineConstructor {}; + + explicit BasePromise( + scoped_refptr<internal::AbstractPromise> abstract_promise); + + // We want this to be inlined to reduce binary size for the Promise<> + // constructor. Its a template to bypass ChromiumStyle plugin which otherwise + // insists this is out of line. + template <typename T> + explicit BasePromise(internal::PassedPromise&& passed_promise, + T InlineConstructor) + : abstract_promise_(passed_promise.Release(), subtle::kAdoptRefTag) {} + + scoped_refptr<internal::AbstractPromise> abstract_promise_; +}; + } // namespace internal + +// Wrapper around scoped_refptr<base::internal::AbstractPromise> which is +// intended for use by TaskRunner implementations. +class BASE_EXPORT WrappedPromise { + public: + WrappedPromise(); + + explicit WrappedPromise(scoped_refptr<internal::AbstractPromise> promise); + + WrappedPromise(const WrappedPromise& other); + WrappedPromise(WrappedPromise&& other) noexcept; + + WrappedPromise& operator=(const WrappedPromise& other); + WrappedPromise& operator=(WrappedPromise&& other) noexcept; + + explicit WrappedPromise(internal::PassedPromise&& passed_promise); + + // Constructs a promise to run |task|. + WrappedPromise(const Location& from_here, OnceClosure task); + + // If the WrappedPromise hasn't been executed, cleared or taken by + // TakeForTesting, it will be canceled to prevent memory leaks of dependent + // tasks that will never run. + ~WrappedPromise(); + + // Returns true if the promise is not null. + operator bool() const { return promise_.get(); } + + bool IsCanceled() const { + DCHECK(promise_); + return promise_->IsCanceled(); + } + + void OnCanceled() { + DCHECK(promise_); + promise_->OnCanceled(); + } + + // Can only be called once, clears |promise_| after execution. + void Execute(); + + // Clears |promise_|. + void Clear(); + + const Location& from_here() const { + DCHECK(promise_); + return promise_->from_here(); + } + + scoped_refptr<internal::AbstractPromise>& GetForTesting() { return promise_; } + + scoped_refptr<internal::AbstractPromise> TakeForTesting() { + return std::move(promise_); + } + + private: + template <typename ResolveType, typename RejectType> + friend class Promise; + + template <typename T, typename... Args> + friend class internal::PromiseCallbackHelper; + + friend class Promises; + + scoped_refptr<internal::AbstractPromise> promise_; +}; + } // namespace base #endif // BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_ diff --git a/chromium/base/task/promise/abstract_promise_unittest.cc b/chromium/base/task/promise/abstract_promise_unittest.cc index ea8d31fd898..5cbe4498d54 100644 --- a/chromium/base/task/promise/abstract_promise_unittest.cc +++ b/chromium/base/task/promise/abstract_promise_unittest.cc @@ -238,10 +238,13 @@ class AbstractPromiseTest : public testing::Test { #endif std::move(settings.callback)); - return AbstractPromise::Create( - settings.task_runner, settings.from_here, - std::move(settings.prerequisites), settings.reject_policy, - DependentList::ConstructUnresolved(), std::move(executor_data)); + return WrappedPromise(AbstractPromise::Create( + settings.task_runner, settings.from_here, + std::move(settings.prerequisites), + settings.reject_policy, + DependentList::ConstructUnresolved(), + std::move(executor_data))) + .TakeForTesting(); } PromiseSettings settings; @@ -293,7 +296,7 @@ class AbstractPromiseTest : public testing::Test { PromiseSettingsBuilder AllPromise( Location from_here, - std::vector<internal::DependentList::Node> prerequisite_list) { + std::vector<DependentList::Node> prerequisite_list) { PromiseSettingsBuilder builder( from_here, std::make_unique<AbstractPromise::AdjacencyList>( std::move(prerequisite_list))); diff --git a/chromium/base/task/promise/dependent_list.h b/chromium/base/task/promise/dependent_list.h index 020bdbfc77f..3245c1cb48f 100644 --- a/chromium/base/task/promise/dependent_list.h +++ b/chromium/base/task/promise/dependent_list.h @@ -59,7 +59,7 @@ class BASE_EXPORT DependentList { // Align Node on an 8-byte boundary to ensure the first 3 bits are 0 and can // be used to store additional state (see static_asserts below). - class BASE_EXPORT alignas(8) Node { + class BASE_EXPORT ALIGNAS(8) Node { public: Node(); explicit Node(Node&& other) noexcept; diff --git a/chromium/base/task/promise/finally_executor.h b/chromium/base/task/promise/finally_executor.h index 7dc1c798a5c..a80f81c5ee9 100644 --- a/chromium/base/task/promise/finally_executor.h +++ b/chromium/base/task/promise/finally_executor.h @@ -43,9 +43,15 @@ class FinallyExecutor { void Execute(AbstractPromise* promise) { AbstractPromise* prerequisite = promise->GetOnlyPrerequisite(); - CallbackT* resolve_executor = static_cast<CallbackT*>(&common_.callback_); - RunHelper<CallbackT, void, ResolveStorage, RejectStorage>::Run( - std::move(*resolve_executor), prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingCB = typename ToRepeatingCallback<CallbackT>::value; + RepeatingCB* resolve_executor = + static_cast<RepeatingCB*>(&common_.callback_); + RunHelper<RepeatingCB, void, ResolveStorage, RejectStorage>::Run( + *resolve_executor, prerequisite, promise); } #if DCHECK_IS_ON() diff --git a/chromium/base/task/promise/helpers.cc b/chromium/base/task/promise/helpers.cc index 8a68a123956..f100cac0c29 100644 --- a/chromium/base/task/promise/helpers.cc +++ b/chromium/base/task/promise/helpers.cc @@ -11,32 +11,11 @@ namespace base { namespace internal { -PromiseHolder::PromiseHolder(scoped_refptr<AbstractPromise> promise) - : promise_(std::move(promise)) {} - -PromiseHolder::~PromiseHolder() { - // Detect if the promise was not executed and if so cancel to ensure memory - // is released. - if (promise_) - promise_->OnCanceled(); -} - -PromiseHolder::PromiseHolder(PromiseHolder&& other) - : promise_(std::move(other.promise_)) {} - -scoped_refptr<AbstractPromise> PromiseHolder::Unwrap() const { - return std::move(promise_); -} - -scoped_refptr<TaskRunner> GetCurrentSequence() { - return SequencedTaskRunnerHandle::Get(); -} - DoNothing ToCallbackBase(DoNothing task) { return task; } -scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite( +PassedPromise ConstructAbstractPromiseWithSinglePrerequisite( const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, AbstractPromise* prerequisite, @@ -46,26 +25,35 @@ scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite( if (!prerequisite) { // Ensure the destructor for |executor_data| runs. PromiseExecutor dummy_executor(std::move(executor_data)); - return nullptr; + return PassedPromise(); } - return AbstractPromise::Create( + return PassedPromise(AbstractPromise::Create( task_runner, from_here, std::make_unique<AbstractPromise::AdjacencyList>(prerequisite), RejectPolicy::kMustCatchRejection, - internal::DependentList::ConstructUnresolved(), std::move(executor_data)); + internal::DependentList::ConstructUnresolved(), + std::move(executor_data))); } -scoped_refptr<AbstractPromise> ConstructManualPromiseResolverPromise( +PassedPromise ConstructHereAbstractPromiseWithSinglePrerequisite( const Location& from_here, - RejectPolicy reject_policy, - bool can_resolve, - bool can_reject) { - return AbstractPromise::CreateNoPrerequisitePromise( + AbstractPromise* prerequisite, + internal::PromiseExecutor::Data&& executor_data) noexcept { + return ConstructAbstractPromiseWithSinglePrerequisite( + SequencedTaskRunnerHandle::Get(), from_here, prerequisite, + std::move(executor_data)); +} + +PassedPromise ConstructManualPromiseResolverPromise(const Location& from_here, + RejectPolicy reject_policy, + bool can_resolve, + bool can_reject) { + return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, reject_policy, internal::DependentList::ConstructUnresolved(), internal::PromiseExecutor::Data( in_place_type_t<internal::NoOpPromiseExecutor>(), can_resolve, - can_reject)); + can_reject))); } } // namespace internal diff --git a/chromium/base/task/promise/helpers.h b/chromium/base/task/promise/helpers.h index 29cb49011ab..87d9ff64bec 100644 --- a/chromium/base/task/promise/helpers.h +++ b/chromium/base/task/promise/helpers.h @@ -19,11 +19,6 @@ class DoNothing; namespace internal { -// A wrapper around SequencedTaskRunnerHandle::Get(). This file is included by -// base/task_runner.h which means we can't include anything that depends on -// that! -scoped_refptr<TaskRunner> BASE_EXPORT GetCurrentSequence(); - template <typename T> using ToNonVoidT = std::conditional_t<std::is_void<T>::value, Void, T>; @@ -412,9 +407,26 @@ class ArgMoveSemanticsHelper { } }; +// Helper for converting a callback to its repeating variant. +template <typename Cb> +struct ToRepeatingCallback; + +template <typename Cb> +struct ToRepeatingCallback<OnceCallback<Cb>> { + using value = RepeatingCallback<Cb>; +}; + +template <typename Cb> +struct ToRepeatingCallback<RepeatingCallback<Cb>> { + using value = RepeatingCallback<Cb>; +}; + // Helper for running a promise callback and storing the result if any. // -// Callback = signature of the callback to execute, +// Callback = signature of the callback to execute. Note we use repeating +// callbacks to avoid the binary size overhead of a once callback which will +// generate a destructor which is redundant because we overwrite the executor +// with the promise result which also triggers the destructor. // ArgStorageType = type of the callback parameter (or void if none) // ResolveStorage = type to use for resolve, usually Resolved<T>. // RejectStorage = type to use for reject, usually Rejected<T>. @@ -431,18 +443,18 @@ template <typename CbResult, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult(CbArg)>, +struct RunHelper<RepeatingCallback<CbResult(CbArg)>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult(CbArg)>; + using Callback = RepeatingCallback<CbResult(CbArg)>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - result, std::move(executor).Run( - ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg))); + result, + executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg))); } }; @@ -451,19 +463,18 @@ template <typename CbArg, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<void(CbArg)>, +struct RunHelper<RepeatingCallback<void(CbArg)>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<void(CbArg)>; + using Callback = RepeatingCallback<void(CbArg)>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { static_assert(std::is_void<typename ResolveStorage::Type>::value, ""); - std::move(executor).Run( - ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)); - result->emplace(Resolved<void>()); + executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)); + result->EmplaceResolvedVoid(); } }; @@ -472,17 +483,17 @@ template <typename CbResult, typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult()>, +struct RunHelper<RepeatingCallback<CbResult()>, ArgStorageType, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult()>; + using Callback = RepeatingCallback<CbResult()>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { - EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - result, std::move(executor).Run()); + EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(result, + executor.Run()); } }; @@ -490,16 +501,16 @@ struct RunHelper<OnceCallback<CbResult()>, template <typename ArgStorageType, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<void()>, +struct RunHelper<RepeatingCallback<void()>, ArgStorageType, ResolveStorage, RejectStorage> { - static void Run(OnceCallback<void()>&& executor, + static void Run(const RepeatingCallback<void()>& executor, AbstractPromise* arg, AbstractPromise* result) { static_assert(std::is_void<typename ResolveStorage::Type>::value, ""); - std::move(executor).Run(); - result->emplace(Resolved<void>()); + executor.Run(); + result->EmplaceResolvedVoid(); } }; @@ -538,79 +549,51 @@ template <typename CbResult, typename... CbArgs, typename ResolveStorage, typename RejectStorage> -struct RunHelper<OnceCallback<CbResult(CbArgs...)>, +struct RunHelper<RepeatingCallback<CbResult(CbArgs...)>, Resolved<std::tuple<CbArgs...>>, ResolveStorage, RejectStorage> { - using Callback = OnceCallback<CbResult(CbArgs...)>; + using Callback = RepeatingCallback<CbResult(CbArgs...)>; using StorageType = Resolved<std::tuple<CbArgs...>>; using IndexSequence = std::index_sequence_for<CbArgs...>; - static void Run(Callback&& executor, + static void Run(const Callback& executor, AbstractPromise* arg, AbstractPromise* result) { AbstractPromise::ValueHandle value = arg->TakeValue(); std::tuple<CbArgs...>& tuple = value.value().Get<StorageType>()->value; - RunInternal(std::move(executor), tuple, result, + RunInternal(executor, tuple, result, std::integral_constant<bool, std::is_void<CbResult>::value>(), IndexSequence{}); } private: template <typename Callback, size_t... Indices> - static void RunInternal(Callback&& executor, + static void RunInternal(const Callback& executor, std::tuple<CbArgs...>& tuple, AbstractPromise* result, std::false_type void_result, std::index_sequence<Indices...>) { - EmplaceHelper<ResolveStorage, RejectStorage>::Emplace( - std::move(executor).Run( - TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, - Indices>::Get(tuple)...)); + EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(executor.Run( + TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, + Indices>::Get(tuple)...)); } template <typename Callback, size_t... Indices> - static void RunInternal(Callback&& executor, + static void RunInternal(const Callback& executor, std::tuple<CbArgs...>& tuple, AbstractPromise* result, std::true_type void_result, std::index_sequence<Indices...>) { - std::move(executor).Run( - TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, - Indices>::Get(tuple)...); - result->emplace(Resolved<void>()); + executor.Run(TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>, + Indices>::Get(tuple)...); + result->EmplaceResolvedVoid(); } }; -// For use with base::Bind*. Cancels the promise if the callback was not run by -// the time the callback is deleted. -class BASE_EXPORT PromiseHolder { - public: - explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise); - - ~PromiseHolder(); - - PromiseHolder(PromiseHolder&& other); - - scoped_refptr<internal::AbstractPromise> Unwrap() const; - - private: - mutable scoped_refptr<internal::AbstractPromise> promise_; -}; - -} // namespace internal - -template <> -struct BindUnwrapTraits<internal::PromiseHolder> { - static scoped_refptr<internal::AbstractPromise> Unwrap( - const internal::PromiseHolder& o) { - return o.Unwrap(); - } -}; - -namespace internal { - -// Used by ManualPromiseResolver<> to generate callbacks. +// Used by ManualPromiseResolver<> to generate callbacks. Note the use of +// WrappedPromise, this is necessary because we want to cancel the promise (to +// release memory) if the callback gets deleted without having being run. template <typename T, typename... Args> class PromiseCallbackHelper { public: @@ -624,7 +607,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnResolved(); }, - PromiseHolder(promise)); + promise); } static RepeatingCallback GetRepeatingResolveCallback( @@ -635,7 +618,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnResolved(); }, - PromiseHolder(promise)); + promise); } static Callback GetRejectCallback(scoped_refptr<AbstractPromise>& promise) { @@ -645,7 +628,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnRejected(); }, - PromiseHolder(promise)); + promise); } static RepeatingCallback GetRepeatingRejectCallback( @@ -656,7 +639,7 @@ class PromiseCallbackHelper { std::forward<Args>(args)...); promise->OnRejected(); }, - PromiseHolder(promise)); + promise); } }; @@ -679,8 +662,7 @@ struct IsValidPromiseArg<PromiseType&, CallbackArgType> { // rejection storage type. template <typename RejectT> struct AllPromiseRejectHelper { - static void Reject(AbstractPromise* result, - const scoped_refptr<AbstractPromise>& prerequisite) { + static void Reject(AbstractPromise* result, AbstractPromise* prerequisite) { result->emplace(scoped_refptr<AbstractPromise>(prerequisite)); } }; @@ -709,14 +691,20 @@ CallbackBase&& ToCallbackBase(const CallbackT&& task) { // Helps reduce template bloat by moving AbstractPromise construction out of // line. -scoped_refptr<AbstractPromise> BASE_EXPORT -ConstructAbstractPromiseWithSinglePrerequisite( +PassedPromise BASE_EXPORT ConstructAbstractPromiseWithSinglePrerequisite( const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, AbstractPromise* prerequsite, - internal::PromiseExecutor::Data&& executor_data) noexcept; + PromiseExecutor::Data&& executor_data) noexcept; + +// Like ConstructAbstractPromiseWithSinglePrerequisite except tasks are posted +// onto SequencedTaskRunnerHandle::Get(). +PassedPromise BASE_EXPORT ConstructHereAbstractPromiseWithSinglePrerequisite( + const Location& from_here, + AbstractPromise* prerequsite, + PromiseExecutor::Data&& executor_data) noexcept; -scoped_refptr<AbstractPromise> BASE_EXPORT +PassedPromise BASE_EXPORT ConstructManualPromiseResolverPromise(const Location& from_here, RejectPolicy reject_policy, bool can_resolve, diff --git a/chromium/base/task/promise/helpers_unittest.cc b/chromium/base/task/promise/helpers_unittest.cc index afdc9e7079e..f13fb5c8c7a 100644 --- a/chromium/base/task/promise/helpers_unittest.cc +++ b/chromium/base/task/promise/helpers_unittest.cc @@ -187,8 +187,9 @@ TEST(EmplaceHelper, EmplacePromiseResult) { TEST(EmplaceHelper, EmplacePromise) { scoped_refptr<AbstractPromise> promise = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - scoped_refptr<AbstractPromise> curried = DoNothingPromiseBuilder(FROM_HERE); + PassedPromise curried = NoOpPromiseExecutor::Create( + FROM_HERE, false, false, RejectPolicy::kCatchNotRequired); EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace( promise.get(), Promise<int>(std::move(curried))); @@ -243,8 +244,8 @@ TEST(RunHelper, CallbackVoidArgumentIntResult) { scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - RunHelper<OnceCallback<int()>, Resolved<void>, Resolved<int>, - Rejected<std::string>>::Run(BindOnce([]() { return 123; }), + RunHelper<RepeatingCallback<int()>, Resolved<void>, Resolved<int>, + Rejected<std::string>>::Run(BindRepeating([]() { return 123; }), arg.get(), result.get()); EXPECT_EQ(result->value().template Get<Resolved<int>>()->value, 123); @@ -255,8 +256,8 @@ TEST(RunHelper, CallbackVoidArgumentVoidResult) { scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); - RunHelper<OnceCallback<void()>, Resolved<void>, Resolved<void>, - Rejected<std::string>>::Run(BindOnce([]() {}), arg.get(), + RunHelper<RepeatingCallback<void()>, Resolved<void>, Resolved<void>, + Rejected<std::string>>::Run(BindRepeating([]() {}), arg.get(), result.get()); EXPECT_TRUE(result->value().ContainsResolved()); @@ -268,8 +269,8 @@ TEST(RunHelper, CallbackIntArgumentIntResult) { DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); arg->emplace(Resolved<int>(123)); - RunHelper<OnceCallback<int(int)>, Resolved<int>, Resolved<int>, - Rejected<std::string>>::Run(BindOnce([](int value) { + RunHelper<RepeatingCallback<int(int)>, Resolved<int>, Resolved<int>, + Rejected<std::string>>::Run(BindRepeating([](int value) { return value + 1; }), arg.get(), result.get()); @@ -284,7 +285,7 @@ TEST(RunHelper, CallbackIntArgumentArgumentVoidResult) { arg->emplace(Resolved<int>(123)); int value; - RunHelper<OnceCallback<void(int)>, Resolved<int>, Resolved<void>, + RunHelper<RepeatingCallback<void(int)>, Resolved<int>, Resolved<void>, Rejected<std::string>>::Run(BindLambdaForTesting([&](int arg) { value = arg; }), diff --git a/chromium/base/task/promise/no_op_promise_executor.cc b/chromium/base/task/promise/no_op_promise_executor.cc index 74a5bcc57c8..e168ed91007 100644 --- a/chromium/base/task/promise/no_op_promise_executor.cc +++ b/chromium/base/task/promise/no_op_promise_executor.cc @@ -45,15 +45,14 @@ bool NoOpPromiseExecutor::CanReject() const { void NoOpPromiseExecutor::Execute(AbstractPromise* promise) {} // static -scoped_refptr<internal::AbstractPromise> NoOpPromiseExecutor::Create( - Location from_here, - bool can_resolve, - bool can_reject, - RejectPolicy reject_policy) { - return AbstractPromise::CreateNoPrerequisitePromise( +PassedPromise NoOpPromiseExecutor::Create(Location from_here, + bool can_resolve, + bool can_reject, + RejectPolicy reject_policy) { + return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, reject_policy, DependentList::ConstructUnresolved(), PromiseExecutor::Data(in_place_type_t<NoOpPromiseExecutor>(), can_resolve, - can_reject)); + can_reject))); } } // namespace internal diff --git a/chromium/base/task/promise/no_op_promise_executor.h b/chromium/base/task/promise/no_op_promise_executor.h index 005ee8c0130..13f8ef0891b 100644 --- a/chromium/base/task/promise/no_op_promise_executor.h +++ b/chromium/base/task/promise/no_op_promise_executor.h @@ -21,10 +21,10 @@ class BASE_EXPORT NoOpPromiseExecutor { static constexpr PromiseExecutor::PrerequisitePolicy kPrerequisitePolicy = PromiseExecutor::PrerequisitePolicy::kNever; - static scoped_refptr<AbstractPromise> Create(Location from_here, - bool can_resolve, - bool can_reject, - RejectPolicy reject_policy); + static PassedPromise Create(Location from_here, + bool can_resolve, + bool can_reject, + RejectPolicy reject_policy); PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const; bool IsCancelled() const; diff --git a/chromium/base/task/promise/post_task_executor.h b/chromium/base/task/promise/post_task_executor.h index c018113de63..e3882ba1712 100644 --- a/chromium/base/task/promise/post_task_executor.h +++ b/chromium/base/task/promise/post_task_executor.h @@ -52,10 +52,14 @@ class PostTaskExecutor { static_assert(sizeof(CallbackBase) == sizeof(OnceCallback<ReturnType()>), "We assume it's possible to cast from CallbackBase to " "OnceCallback<ReturnType()>"); - OnceCallback<ReturnType()>* task = - static_cast<OnceCallback<ReturnType()>*>(&task_); - internal::RunHelper<OnceCallback<ReturnType()>, void, ResolveStorage, - RejectStorage>::Run(std::move(*task), nullptr, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + RepeatingCallback<ReturnType()>* task = + static_cast<RepeatingCallback<ReturnType()>*>(&task_); + internal::RunHelper<RepeatingCallback<ReturnType()>, void, ResolveStorage, + RejectStorage>::Run(*task, nullptr, promise); } private: diff --git a/chromium/base/task/promise/post_task_executor_unittest.cc b/chromium/base/task/promise/post_task_executor_unittest.cc index 8e86aaefad4..1d3fc57e4d0 100644 --- a/chromium/base/task/promise/post_task_executor_unittest.cc +++ b/chromium/base/task/promise/post_task_executor_unittest.cc @@ -16,9 +16,8 @@ namespace internal { class PostTaskExecutorTest : public testing::Test { public: template <typename CallbackT> - scoped_refptr<internal::AbstractPromise> CreatePostTaskPromise( - const Location& from_here, - CallbackT&& task) { + WrappedPromise CreatePostTaskPromise(const Location& from_here, + CallbackT&& task) { // Extract properties from |task| callback. using CallbackTraits = CallbackTraits<std::decay_t<CallbackT>>; @@ -27,20 +26,20 @@ class PostTaskExecutorTest : public testing::Test { internal::PostTaskExecutor<typename CallbackTraits::ReturnType>>(), internal::ToCallbackBase(std::move(task))); - return AbstractPromise::CreateNoPrerequisitePromise( + return WrappedPromise(AbstractPromise::CreateNoPrerequisitePromise( from_here, RejectPolicy::kMustCatchRejection, internal::DependentList::ConstructUnresolved(), - std::move(executor_data)); + std::move(executor_data))); } }; TEST_F(PostTaskExecutorTest, OnceClosure) { bool run = false; - scoped_refptr<AbstractPromise> p = CreatePostTaskPromise( + WrappedPromise p = CreatePostTaskPromise( FROM_HERE, BindOnce([](bool* run) { *run = true; }, &run)); - p->Execute(); + p.Execute(); EXPECT_TRUE(run); } @@ -48,20 +47,19 @@ TEST_F(PostTaskExecutorTest, OnceClosure) { TEST_F(PostTaskExecutorTest, RepeatingClosure) { bool run = false; - scoped_refptr<AbstractPromise> p = CreatePostTaskPromise( + WrappedPromise p = CreatePostTaskPromise( FROM_HERE, BindRepeating([](bool* run) { *run = true; }, &run)); - p->Execute(); + p.Execute(); EXPECT_TRUE(run); } TEST_F(PostTaskExecutorTest, DoNothing) { // Check it compiles and the executor doesn't crash when run. - scoped_refptr<AbstractPromise> p = - CreatePostTaskPromise(FROM_HERE, DoNothing()); + WrappedPromise p = CreatePostTaskPromise(FROM_HERE, DoNothing()); - p->Execute(); + p.Execute(); } } // namespace internal diff --git a/chromium/base/task/promise/promise.h b/chromium/base/task/promise/promise.h index 4f2275ea183..467f74cb8e4 100644 --- a/chromium/base/task/promise/promise.h +++ b/chromium/base/task/promise/promise.h @@ -30,12 +30,12 @@ BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner( // callback will be posted immediately, otherwise it has to wait. // // Promise<> is copyable, moveable and thread safe. Under the hood -// internal::AbstractPromise is refcounted so retaining multiple Promises<> will +// AbstractPromise is refcounted so retaining multiple Promises<> will // prevent that part of the promise graph from being released. template <typename ResolveType, typename RejectType = NoReject> -class Promise { +class Promise : public internal::BasePromise { public: - Promise() : abstract_promise_(nullptr) {} + Promise() = default; static_assert( !std::is_reference<ResolveType>::value || @@ -49,16 +49,17 @@ class Promise { explicit Promise( scoped_refptr<internal::AbstractPromise> abstract_promise) noexcept - : abstract_promise_(std::move(abstract_promise)) {} + : BasePromise(std::move(abstract_promise)) {} - ~Promise() = default; + // Every PostTask calls this constructor so we need to be careful to avoid + // unnecessary binary bloat. + explicit Promise(internal::PassedPromise passed_promise) noexcept + : BasePromise(std::move(passed_promise), + BasePromise::InlineConstructor()) {} - operator bool() const { return !!abstract_promise_; } + ~Promise() = default; - bool IsCancelledForTesting() const { - DCHECK(abstract_promise_); - return abstract_promise_->IsCanceled(); - } + bool IsCancelledForTesting() const { return abstract_promise_->IsCanceled(); } // Waits until the promise has settled and if resolved it returns the resolved // value. @@ -75,8 +76,10 @@ class Promise { } DCHECK(abstract_promise_->IsResolved()) << "Can't take resolved value, promise wasn't resolved."; - return std::move( - abstract_promise_->TakeValue().value().Get<Resolved<T>>()->value); + return std::move(abstract_promise_->TakeValue() + .value() + .template Get<Resolved<T>>() + ->value); } // Waits until the promise has settled and if rejected it returns the rejected @@ -95,8 +98,10 @@ class Promise { abstract_promise_->IgnoreUncaughtCatchForTesting(); DCHECK(abstract_promise_->IsRejected()) << "Can't take rejected value, promise wasn't rejected."; - return std::move( - abstract_promise_->TakeValue().value().Get<Rejected<T>>()->value); + return std::move(abstract_promise_->TakeValue() + .value() + .template Get<Rejected<T>>() + ->value); } bool IsResolvedForTesting() const { @@ -122,22 +127,22 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename RejectCb> + template <typename CatchCb> auto CatchOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - RejectCb on_reject) noexcept { + CatchCb on_reject) noexcept { DCHECK(!on_reject.is_null()); // Extract properties from the |on_reject| callback. - using RejectCallbackTraits = internal::CallbackTraits<RejectCb>; - using RejectCallbackArgT = typename RejectCallbackTraits::ArgType; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = internal::PromiseCombiner<ResolveType, NoReject, // We've caught the reject case. - typename RejectCallbackTraits::ResolveType, - typename RejectCallbackTraits::RejectType>; + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -148,13 +153,13 @@ class Promise { static_assert(ReturnedPromiseTraits::valid, "Ambiguous promise resolve type"); static_assert( - internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value || - std::is_void<RejectCallbackArgT>::value, + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, "|on_reject| callback must accept Promise::RejectType or void."); static_assert( - !std::is_reference<RejectCallbackArgT>::value || - std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value, + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -163,7 +168,7 @@ class Promise { internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< OnceClosure, // Never called. - OnceCallback<typename RejectCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, internal::NoCallback, RejectType, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), @@ -171,18 +176,59 @@ class Promise { internal::ToCallbackBase(std::move(on_reject))))); } - template <typename RejectCb> + template <typename CatchCb> auto CatchOn(const TaskTraits& traits, const Location& from_here, - RejectCb&& on_reject) noexcept { + CatchCb&& on_reject) noexcept { return CatchOn(CreateTaskRunner(traits), from_here, - std::forward<RejectCb>(on_reject)); + std::forward<CatchCb>(on_reject)); } - template <typename RejectCb> - auto CatchHere(const Location& from_here, RejectCb&& on_reject) noexcept { - return CatchOn(internal::GetCurrentSequence(), from_here, - std::forward<RejectCb>(on_reject)); + template <typename CatchCb> + auto CatchHere(const Location& from_here, CatchCb&& on_reject) noexcept { + DCHECK(!on_reject.is_null()); + + // Extract properties from the |on_reject| callback. + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<ResolveType, + NoReject, // We've caught the reject case. + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + static_assert(!std::is_same<NoReject, RejectType>::value, + "Can't catch a NoReject promise."); + + // Check we wouldn't need to return Promise<Variant<...>, ...> + static_assert(ReturnedPromiseTraits::valid, + "Ambiguous promise resolve type"); + static_assert( + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, + "|on_reject| callback must accept Promise::RejectType or void."); + + static_assert( + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceClosure, // Never called. + OnceCallback<typename CatchCallbackTraits::SignatureType>, + internal::NoCallback, RejectType, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + OnceClosure(), + internal::ToCallbackBase(std::move(on_reject))))); } // A task to execute |on_resolve| is posted on |task_runner| as soon as this @@ -198,23 +244,22 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename ResolveCb> + template <typename ThenCb> auto ThenOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - ResolveCb on_resolve) noexcept { + ThenCb on_resolve) noexcept { DCHECK(!on_resolve.is_null()); // Extract properties from the |on_resolve| callback. - using ResolveCallbackTraits = - internal::CallbackTraits<std::decay_t<ResolveCb>>; - using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType; + using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = internal::PromiseCombiner<NoResolve, // We've caught the resolve case. RejectType, - typename ResolveCallbackTraits::ResolveType, - typename ResolveCallbackTraits::RejectType>; + typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -223,13 +268,13 @@ class Promise { "Ambiguous promise reject type"); static_assert( - internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value || - std::is_void<ResolveCallbackArgT>::value, + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, "|on_resolve| callback must accept Promise::ResolveType or void."); static_assert( - !std::is_reference<ResolveCallbackArgT>::value || - std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value, + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -237,7 +282,7 @@ class Promise { task_runner, from_here, abstract_promise_.get(), internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< - OnceCallback<typename ResolveCallbackTraits::SignatureType>, + OnceCallback<typename ThenCallbackTraits::SignatureType>, OnceClosure, ResolveType, internal::NoCallback, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), @@ -245,18 +290,56 @@ class Promise { OnceClosure()))); } - template <typename ResolveCb> + template <typename ThenCb> auto ThenOn(const TaskTraits& traits, const Location& from_here, - ResolveCb&& on_resolve) noexcept { + ThenCb&& on_resolve) noexcept { return ThenOn(CreateTaskRunner(traits), from_here, - std::forward<ResolveCb>(on_resolve)); + std::forward<ThenCb>(on_resolve)); } - template <typename ResolveCb> - auto ThenHere(const Location& from_here, ResolveCb&& on_resolve) noexcept { - return ThenOn(internal::GetCurrentSequence(), from_here, - std::forward<ResolveCb>(on_resolve)); + template <typename ThenCb> + auto ThenHere(const Location& from_here, ThenCb&& on_resolve) noexcept { + DCHECK(!on_resolve.is_null()); + + // Extract properties from the |on_resolve| callback. + using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<NoResolve, // We've caught the resolve case. + RejectType, + typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + // Check we wouldn't need to return Promise<..., Variant<...>> + static_assert(ReturnedPromiseTraits::valid, + "Ambiguous promise reject type"); + + static_assert( + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, + "|on_resolve| callback must accept Promise::ResolveType or void."); + + static_assert( + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceClosure, ResolveType, internal::NoCallback, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(on_resolve)), + OnceClosure()))); } // A task to execute |on_reject| is posted on |task_runner| as soon as this @@ -279,26 +362,26 @@ class Promise { // // |task_runner| is const-ref to avoid bloat due the destructor (which posts a // task). - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenOn(const scoped_refptr<TaskRunner>& task_runner, const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { + ThenCb on_resolve, + CatchCb on_reject) noexcept { DCHECK(!on_resolve.is_null()); DCHECK(!on_reject.is_null()); // Extract properties from the |on_resolve| and |on_reject| callbacks. - using ResolveCallbackTraits = internal::CallbackTraits<ResolveCb>; - using RejectCallbackTraits = internal::CallbackTraits<RejectCb>; - using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType; - using RejectCallbackArgT = typename RejectCallbackTraits::ArgType; + using ThenCallbackTraits = internal::CallbackTraits<ThenCb>; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; // Compute the resolve and reject types of the returned Promise. using ReturnedPromiseTraits = - internal::PromiseCombiner<typename ResolveCallbackTraits::ResolveType, - typename ResolveCallbackTraits::RejectType, - typename RejectCallbackTraits::ResolveType, - typename RejectCallbackTraits::RejectType>; + internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType, + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; @@ -310,23 +393,23 @@ class Promise { "compatible types."); static_assert( - internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value || - std::is_void<ResolveCallbackArgT>::value, + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, "|on_resolve| callback must accept Promise::ResolveType or void."); static_assert( - internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value || - std::is_void<RejectCallbackArgT>::value, + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, "|on_reject| callback must accept Promise::RejectType or void."); static_assert( - !std::is_reference<ResolveCallbackArgT>::value || - std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value, + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); static_assert( - !std::is_reference<RejectCallbackArgT>::value || - std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value, + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, "Google C++ Style: References in function parameters must be const."); return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( @@ -334,31 +417,84 @@ class Promise { task_runner, from_here, abstract_promise_.get(), internal::PromiseExecutor::Data( in_place_type_t<internal::ThenAndCatchExecutor< - OnceCallback<typename ResolveCallbackTraits::SignatureType>, - OnceCallback<typename RejectCallbackTraits::SignatureType>, + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>, Rejected<ReturnedPromiseRejectT>>>(), internal::ToCallbackBase(std::move(on_resolve)), internal::ToCallbackBase(std::move(on_reject))))); } - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenOn(const TaskTraits& traits, const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { + ThenCb on_resolve, + CatchCb on_reject) noexcept { return ThenOn(CreateTaskRunner(traits), from_here, - std::forward<ResolveCb>(on_resolve), - std::forward<RejectCb>(on_reject)); + std::forward<ThenCb>(on_resolve), + std::forward<CatchCb>(on_reject)); } - template <typename ResolveCb, typename RejectCb> + template <typename ThenCb, typename CatchCb> auto ThenHere(const Location& from_here, - ResolveCb on_resolve, - RejectCb on_reject) noexcept { - return ThenOn(internal::GetCurrentSequence(), from_here, - std::forward<ResolveCb>(on_resolve), - std::forward<RejectCb>(on_reject)); + ThenCb on_resolve, + CatchCb on_reject) noexcept { + DCHECK(!on_resolve.is_null()); + DCHECK(!on_reject.is_null()); + + // Extract properties from the |on_resolve| and |on_reject| callbacks. + using ThenCallbackTraits = internal::CallbackTraits<ThenCb>; + using CatchCallbackTraits = internal::CallbackTraits<CatchCb>; + using ThenCallbackArgT = typename ThenCallbackTraits::ArgType; + using CatchCallbackArgT = typename CatchCallbackTraits::ArgType; + + // Compute the resolve and reject types of the returned Promise. + using ReturnedPromiseTraits = + internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType, + typename ThenCallbackTraits::RejectType, + typename CatchCallbackTraits::ResolveType, + typename CatchCallbackTraits::RejectType>; + using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType; + using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType; + + static_assert(!std::is_same<NoReject, RejectType>::value, + "Can't catch a NoReject promise."); + + static_assert(ReturnedPromiseTraits::valid, + "|on_resolve| callback and |on_resolve| callback must return " + "compatible types."); + + static_assert( + internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value || + std::is_void<ThenCallbackArgT>::value, + "|on_resolve| callback must accept Promise::ResolveType or void."); + + static_assert( + internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value || + std::is_void<CatchCallbackArgT>::value, + "|on_reject| callback must accept Promise::RejectType or void."); + + static_assert( + !std::is_reference<ThenCallbackArgT>::value || + std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + static_assert( + !std::is_reference<CatchCallbackArgT>::value || + std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value, + "Google C++ Style: References in function parameters must be const."); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::ThenAndCatchExecutor< + OnceCallback<typename ThenCallbackTraits::SignatureType>, + OnceCallback<typename CatchCallbackTraits::SignatureType>, + ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(on_resolve)), + internal::ToCallbackBase(std::move(on_reject))))); } // A task to execute |finally_callback| on |task_runner| is posted after the @@ -405,8 +541,24 @@ class Promise { template <typename FinallyCb> auto FinallyHere(const Location& from_here, FinallyCb finally_callback) noexcept { - return FinallyOn(internal::GetCurrentSequence(), from_here, - std::move(finally_callback)); + // Extract properties from |finally_callback| callback. + using CallbackTraits = internal::CallbackTraits<FinallyCb>; + using ReturnedPromiseResolveT = typename CallbackTraits::ResolveType; + using ReturnedPromiseRejectT = typename CallbackTraits::RejectType; + + using CallbackArgT = typename CallbackTraits::ArgType; + static_assert(std::is_void<CallbackArgT>::value, + "|finally_callback| callback must have no arguments"); + + return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>( + ConstructHereAbstractPromiseWithSinglePrerequisite( + from_here, abstract_promise_.get(), + internal::PromiseExecutor::Data( + in_place_type_t<internal::FinallyExecutor< + OnceCallback<typename CallbackTraits::ReturnType()>, + Resolved<ReturnedPromiseResolveT>, + Rejected<ReturnedPromiseRejectT>>>(), + internal::ToCallbackBase(std::move(finally_callback))))); } template <typename... Args> @@ -442,8 +594,6 @@ class Promise { nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection, internal::DependentList::ConstructResolved(), std::move(executor_data))); - promise->emplace(in_place_type_t<Rejected<RejectType>>(), - std::forward<Args>(args)...); return Promise<ResolveType, RejectType>(std::move(promise)); } @@ -454,6 +604,11 @@ class Promise { abstract_promise_->IgnoreUncaughtCatchForTesting(); } + const scoped_refptr<internal::AbstractPromise>& GetScopedRefptrForTesting() + const { + return abstract_promise_; + } + private: template <typename A, typename B> friend class Promise; @@ -471,8 +626,6 @@ class Promise { template <typename A, typename B> friend class ManualPromiseResolver; - - scoped_refptr<internal::AbstractPromise> abstract_promise_; }; // Used for manually resolving and rejecting a Promise. This is for @@ -624,8 +777,8 @@ class Promises { std::vector<internal::DependentList::Node> prerequisite_list( sizeof...(promises)); int i = 0; - for (auto&& p : {promises.abstract_promise_...}) { - prerequisite_list[i++].SetPrerequisite(p.get()); + for (auto&& p : {promises.abstract_promise_.get()...}) { + prerequisite_list[i++].SetPrerequisite(p); } internal::PromiseExecutor::Data executor_data( diff --git a/chromium/base/task/promise/promise_unittest.cc b/chromium/base/task/promise/promise_unittest.cc index bb3dda41cb3..d81adacb1a0 100644 --- a/chromium/base/task/promise/promise_unittest.cc +++ b/chromium/base/task/promise/promise_unittest.cc @@ -106,6 +106,86 @@ TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) { EXPECT_TRUE(delete_reply_flag); } +TEST(PromiseMemoryLeakTest, GetResolveCallbackNeverRun) { + test::TaskEnvironment task_environment_; + OnceCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<int> p(FROM_HERE); + cb = p.GetResolveCallback(); + + p.promise().ThenHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = OnceCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRepeatingResolveCallbackNeverRun) { + test::TaskEnvironment task_environment_; + RepeatingCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<int> p(FROM_HERE); + cb = p.GetRepeatingResolveCallback(); + + p.promise().ThenHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = RepeatingCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRejectCallbackNeverRun) { + test::TaskEnvironment task_environment_; + OnceCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<void, int> p(FROM_HERE); + cb = p.GetRejectCallback(); + + p.promise().CatchHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = OnceCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + +TEST(PromiseMemoryLeakTest, GetRepeatingRejectCallbackNeverRun) { + test::TaskEnvironment task_environment_; + RepeatingCallback<void(int)> cb; + MockObject mock_object; + bool delete_task_flag = false; + + { + ManualPromiseResolver<void, int> p(FROM_HERE); + cb = p.GetRepeatingRejectCallback(); + + p.promise().CatchHere( + FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object), + MakeRefCounted<ObjectToDelete>(&delete_task_flag))); + } + + EXPECT_FALSE(delete_task_flag); + cb = RepeatingCallback<void(int)>(); + EXPECT_TRUE(delete_task_flag); +} + TEST_F(PromiseTest, GetResolveCallbackThen) { ManualPromiseResolver<int> p(FROM_HERE); p.GetResolveCallback().Run(123); @@ -1602,13 +1682,17 @@ TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) { auto p = Promise<void, std::unique_ptr<int>>::CreateRejected( FROM_HERE, std::make_unique<int>(123)); - p.CatchHere(FROM_HERE, - BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); + auto r = p.CatchHere( + FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); EXPECT_DCHECK_DEATH({ p.CatchHere(FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); }); + + // TODO(alexclarke): Temporary, remove when SequenceManager handles promises + // natively. + r.GetScopedRefptrForTesting()->OnCanceled(); #endif } diff --git a/chromium/base/task/promise/promise_value.h b/chromium/base/task/promise/promise_value.h index 32ec758716d..3b1cfc43464 100644 --- a/chromium/base/task/promise/promise_value.h +++ b/chromium/base/task/promise/promise_value.h @@ -40,9 +40,6 @@ struct Resolved { "Can't have Resolved<NoResolve>"); } - Resolved(const Resolved& other) = default; - Resolved(Resolved&& other) = default; - // Conversion constructor accepts any arguments except Resolved<T>. template < typename... Args, @@ -75,9 +72,6 @@ struct Rejected { "Can't have Rejected<NoReject>"); } - Rejected(const Rejected& other) = default; - Rejected(Rejected&& other) = default; - // Conversion constructor accepts any arguments except Rejected<T>. template < typename... Args, diff --git a/chromium/base/task/promise/then_and_catch_executor.cc b/chromium/base/task/promise/then_and_catch_executor.cc index 5f7f8f09ad0..5eb38e99b78 100644 --- a/chromium/base/task/promise/then_and_catch_executor.cc +++ b/chromium/base/task/promise/then_and_catch_executor.cc @@ -8,14 +8,14 @@ namespace base { namespace internal { bool ThenAndCatchExecutorCommon::IsCancelled() const { - if (!resolve_callback_.is_null()) { + if (!then_callback_.is_null()) { // If there is both a resolve and a reject executor they must be canceled // at the same time. - DCHECK(reject_callback_.is_null() || - reject_callback_.IsCancelled() == resolve_callback_.IsCancelled()); - return resolve_callback_.IsCancelled(); + DCHECK(catch_callback_.is_null() || + catch_callback_.IsCancelled() == then_callback_.IsCancelled()); + return then_callback_.IsCancelled(); } - return reject_callback_.IsCancelled(); + return catch_callback_.IsCancelled(); } void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise, @@ -23,16 +23,16 @@ void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise, ExecuteCallback execute_catch) { AbstractPromise* prerequisite = promise->GetOnlyPrerequisite(); if (prerequisite->IsResolved()) { - if (ProcessNullCallback(resolve_callback_, prerequisite, promise)) + if (ProcessNullCallback(then_callback_, prerequisite, promise)) return; - execute_then(prerequisite, promise, &resolve_callback_); + execute_then(prerequisite, promise, &then_callback_); } else { DCHECK(prerequisite->IsRejected()); - if (ProcessNullCallback(reject_callback_, prerequisite, promise)) + if (ProcessNullCallback(catch_callback_, prerequisite, promise)) return; - execute_catch(prerequisite, promise, &reject_callback_); + execute_catch(prerequisite, promise, &catch_callback_); } } diff --git a/chromium/base/task/promise/then_and_catch_executor.h b/chromium/base/task/promise/then_and_catch_executor.h index 56d30ba2a11..7668fc1fbcb 100644 --- a/chromium/base/task/promise/then_and_catch_executor.h +++ b/chromium/base/task/promise/then_and_catch_executor.h @@ -17,11 +17,11 @@ namespace internal { // Exists to reduce template bloat. class BASE_EXPORT ThenAndCatchExecutorCommon { public: - ThenAndCatchExecutorCommon(internal::CallbackBase&& resolve_executor, - internal::CallbackBase&& reject_executor) noexcept - : resolve_callback_(std::move(resolve_executor)), - reject_callback_(std::move(reject_executor)) { - DCHECK(!resolve_callback_.is_null() || !reject_callback_.is_null()); + ThenAndCatchExecutorCommon(internal::CallbackBase&& then_callback, + internal::CallbackBase&& catch_callback) noexcept + : then_callback_(std::move(then_callback)), + catch_callback_(std::move(catch_callback)) { + DCHECK(!then_callback_.is_null() || !catch_callback_.is_null()); } ~ThenAndCatchExecutorCommon() = default; @@ -44,24 +44,23 @@ class BASE_EXPORT ThenAndCatchExecutorCommon { AbstractPromise* arg, AbstractPromise* result); - CallbackBase resolve_callback_; - CallbackBase reject_callback_; + CallbackBase then_callback_; + CallbackBase catch_callback_; }; // Tag signals no callback which is used to eliminate dead code. struct NoCallback {}; -template <typename ResolveOnceCallback, - typename RejectOnceCallback, +template <typename ThenOnceCallback, + typename CatchOnceCallback, typename ArgResolve, typename ArgReject, typename ResolveStorage, typename RejectStorage> class ThenAndCatchExecutor { public: - using ResolveReturnT = - typename CallbackTraits<ResolveOnceCallback>::ReturnType; - using RejectReturnT = typename CallbackTraits<RejectOnceCallback>::ReturnType; + using ThenReturnT = typename CallbackTraits<ThenOnceCallback>::ReturnType; + using CatchReturnT = typename CallbackTraits<CatchOnceCallback>::ReturnType; using PrerequisiteCouldResolve = std::integral_constant<bool, !std::is_same<ArgResolve, NoCallback>::value>; @@ -69,8 +68,8 @@ class ThenAndCatchExecutor { std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>; ThenAndCatchExecutor(CallbackBase&& resolve_callback, - CallbackBase&& reject_callback) noexcept - : common_(std::move(resolve_callback), std::move(reject_callback)) {} + CallbackBase&& catch_callback) noexcept + : common_(std::move(resolve_callback), std::move(catch_callback)) {} bool IsCancelled() const { return common_.IsCancelled(); } @@ -85,29 +84,29 @@ class ThenAndCatchExecutor { #if DCHECK_IS_ON() PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const { - return common_.resolve_callback_.is_null() + return common_.then_callback_.is_null() ? PromiseExecutor::ArgumentPassingType::kNoCallback - : CallbackTraits<ResolveOnceCallback>::argument_passing_type; + : CallbackTraits<ThenOnceCallback>::argument_passing_type; } PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const { - return common_.reject_callback_.is_null() + return common_.catch_callback_.is_null() ? PromiseExecutor::ArgumentPassingType::kNoCallback - : CallbackTraits<RejectOnceCallback>::argument_passing_type; + : CallbackTraits<CatchOnceCallback>::argument_passing_type; } bool CanResolve() const { - return (!common_.resolve_callback_.is_null() && - PromiseCallbackTraits<ResolveReturnT>::could_resolve) || - (!common_.reject_callback_.is_null() && - PromiseCallbackTraits<RejectReturnT>::could_resolve); + return (!common_.then_callback_.is_null() && + PromiseCallbackTraits<ThenReturnT>::could_resolve) || + (!common_.catch_callback_.is_null() && + PromiseCallbackTraits<CatchReturnT>::could_resolve); } bool CanReject() const { - return (!common_.resolve_callback_.is_null() && - PromiseCallbackTraits<ResolveReturnT>::could_reject) || - (!common_.reject_callback_.is_null() && - PromiseCallbackTraits<RejectReturnT>::could_reject); + return (!common_.then_callback_.is_null() && + PromiseCallbackTraits<ThenReturnT>::could_reject) || + (!common_.catch_callback_.is_null() && + PromiseCallbackTraits<CatchReturnT>::could_reject); } #endif @@ -121,8 +120,8 @@ class ThenAndCatchExecutor { static void ExecuteCatch(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback) { - ExecuteCatchInternal(prerequisite, promise, reject_callback, + CallbackBase* catch_callback) { + ExecuteCatchInternal(prerequisite, promise, catch_callback, PrerequisiteCouldReject()); } @@ -130,10 +129,16 @@ class ThenAndCatchExecutor { AbstractPromise* promise, CallbackBase* resolve_callback, std::true_type can_resolve) { - RunHelper<ResolveOnceCallback, Resolved<ArgResolve>, ResolveStorage, - RejectStorage>:: - Run(std::move(*static_cast<ResolveOnceCallback*>(resolve_callback)), - prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingThenCB = + typename ToRepeatingCallback<ThenOnceCallback>::value; + RunHelper< + RepeatingThenCB, Resolved<ArgResolve>, ResolveStorage, + RejectStorage>::Run(*static_cast<RepeatingThenCB*>(resolve_callback), + prerequisite, promise); } static void ExecuteThenInternal(AbstractPromise* prerequisite, @@ -145,17 +150,23 @@ class ThenAndCatchExecutor { static void ExecuteCatchInternal(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback, + CallbackBase* catch_callback, std::true_type can_reject) { - RunHelper<RejectOnceCallback, Rejected<ArgReject>, ResolveStorage, - RejectStorage>:: - Run(std::move(*static_cast<RejectOnceCallback*>(reject_callback)), - prerequisite, promise); + // Internally RunHelper uses const RepeatingCallback<>& to avoid the + // binary size overhead of moving a scoped_refptr<> about. We respect + // the onceness of the callback and RunHelper will overwrite the callback + // with the result. + using RepeatingCatchCB = + typename ToRepeatingCallback<CatchOnceCallback>::value; + RunHelper< + RepeatingCatchCB, Rejected<ArgReject>, ResolveStorage, + RejectStorage>::Run(*static_cast<RepeatingCatchCB*>(catch_callback), + prerequisite, promise); } static void ExecuteCatchInternal(AbstractPromise* prerequisite, AbstractPromise* promise, - CallbackBase* reject_callback, + CallbackBase* catch_callback, std::false_type can_reject) { // |prerequisite| can't reject so don't generate dead code. } |