Commit 0c886bed authored by Nico Weber's avatar Nico Weber Committed by Commit Bot

Revert "Reland: Promises: Implement Promise<> handle with Then and Catch"

This reverts commit 810a8608.

Reason for revert: Tests fail on UBSanVptr bot:
https://ci.chromium.org/p/chromium/builders/ci/ToTLinuxUBSanVptr/6837

All failures look like:

[ RUN      ] PromiseTest.CatchThenVoid
../../base/task/promise/then_and_catch_executor.h:177:5: runtime error: member call on address 0x357fcd736278 which does not point to an object of type 'base::internal::ThenAndCatchExecutor<base::OnceCallback<void ()>, base::OnceCallback<int ()>, base::internal::NoCallback, void, base::Resolved<int>, base::Rejected<base::NoReject> >'
0x357fcd736278: note: object has invalid vptr
 74 55 00 00  7b 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  28 8c 7b cd
              ^~~~~~~~~~~~~~~~~~~~~~~
              invalid vptr


Original change's description:
> Reland: Promises: Implement Promise<> handle with Then and Catch
> 
> Original patch https://crrev.com/c/1599620
> 
> MultiThreadedPromiseTest.CrossThreadThens had a race between the
> resolve on thread b and thread a posting a bunch of tasks including
> the quit closure which would sometimes arrive mid way through
> thread b posting eligible thens.
> 
> 
> 
> Implements Then and Catch on top of AbstractPromise as well as
> ManualPromiseResolver<> which is needed for testing at this
> stage (PostTask doesn't yet return a promise). It's also
> necessary for promises to interface with old style callback
> based libraries which are extremely common in Chromium.
> 
> Design: https://docs.google.com/document/d/1l12PAJgEtlrqTXKiw6mk2cR2jP7FAfCCDr-DGIdiC9w/edit
> 
> TBR=fdoray@chromium.org, etiennep@chromium.org
> 
> Bug: 906125
> Change-Id: I95bbd0f1a709643cd4a813e378b625a91f2abe7a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617769
> Reviewed-by: Alex Clarke <alexclarke@chromium.org>
> Commit-Queue: Alex Clarke <alexclarke@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#661171}

TBR=alexclarke@chromium.org

Change-Id: I7f5d8fb65620b8d0dd44066f6da9d077d1e7edcd
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 906125
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1618803Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Commit-Queue: Nico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661189}
parent 782671d6
...@@ -755,14 +755,9 @@ jumbo_component("base") { ...@@ -755,14 +755,9 @@ jumbo_component("base") {
"task/promise/abstract_promise.h", "task/promise/abstract_promise.h",
"task/promise/dependent_list.cc", "task/promise/dependent_list.cc",
"task/promise/dependent_list.h", "task/promise/dependent_list.h",
"task/promise/helpers.h",
"task/promise/no_op_promise_executor.cc", "task/promise/no_op_promise_executor.cc",
"task/promise/no_op_promise_executor.h", "task/promise/no_op_promise_executor.h",
"task/promise/promise.h",
"task/promise/promise_result.h",
"task/promise/small_unique_object.h", "task/promise/small_unique_object.h",
"task/promise/then_and_catch_executor.cc",
"task/promise/then_and_catch_executor.h",
"task/scoped_set_task_priority_for_current_thread.cc", "task/scoped_set_task_priority_for_current_thread.cc",
"task/scoped_set_task_priority_for_current_thread.h", "task/scoped_set_task_priority_for_current_thread.h",
"task/sequence_manager/associated_thread_id.cc", "task/sequence_manager/associated_thread_id.cc",
...@@ -2636,8 +2631,6 @@ test("base_unittests") { ...@@ -2636,8 +2631,6 @@ test("base_unittests") {
"task/post_task_unittest.cc", "task/post_task_unittest.cc",
"task/promise/abstract_promise_unittest.cc", "task/promise/abstract_promise_unittest.cc",
"task/promise/dependent_list_unittest.cc", "task/promise/dependent_list_unittest.cc",
"task/promise/helpers_unittest.cc",
"task/promise/promise_unittest.cc",
"task/scoped_set_task_priority_for_current_thread_unittest.cc", "task/scoped_set_task_priority_for_current_thread_unittest.cc",
"task/sequence_manager/atomic_flag_set_unittest.cc", "task/sequence_manager/atomic_flag_set_unittest.cc",
"task/sequence_manager/lazily_deallocated_deque_unittest.cc", "task/sequence_manager/lazily_deallocated_deque_unittest.cc",
...@@ -3041,7 +3034,6 @@ if (enable_nocompile_tests) { ...@@ -3041,7 +3034,6 @@ if (enable_nocompile_tests) {
"observer_list_unittest.nc", "observer_list_unittest.nc",
"optional_unittest.nc", "optional_unittest.nc",
"strings/string16_unittest.nc", "strings/string16_unittest.nc",
"task/promise/promise_unittest.nc",
"task/task_traits_extension_unittest.nc", "task/task_traits_extension_unittest.nc",
"task/task_traits_unittest.nc", "task/task_traits_unittest.nc",
"thread_annotations_unittest.nc", "thread_annotations_unittest.nc",
......
...@@ -19,15 +19,14 @@ struct FakeBindState; ...@@ -19,15 +19,14 @@ struct FakeBindState;
namespace internal { namespace internal {
class BaseThenAndCatchExecutor; class CallbackBase;
class CallbackBaseCopyable;
class BindStateBase; class BindStateBase;
template <typename Functor, typename... BoundArgs> template <typename Functor, typename... BoundArgs>
struct BindState; struct BindState;
class CallbackBase;
class CallbackBaseCopyable;
struct BindStateBaseRefCountTraits { struct BindStateBaseRefCountTraits {
static void Destruct(const BindStateBase*); static void Destruct(const BindStateBase*);
}; };
...@@ -136,8 +135,6 @@ class BASE_EXPORT CallbackBase { ...@@ -136,8 +135,6 @@ class BASE_EXPORT CallbackBase {
void Reset(); void Reset();
protected: protected:
friend class BaseThenAndCatchExecutor;
using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
// Returns true if this callback equals |other|. |other| may be null. // Returns true if this callback equals |other|. |other| may be null.
......
...@@ -47,11 +47,6 @@ const AbstractPromise* AbstractPromise::FindNonCurriedAncestor() const { ...@@ -47,11 +47,6 @@ const AbstractPromise* AbstractPromise::FindNonCurriedAncestor() const {
void AbstractPromise::AddAsDependentForAllPrerequisites() { void AbstractPromise::AddAsDependentForAllPrerequisites() {
DCHECK(prerequisites_); DCHECK(prerequisites_);
// Note a curried promise will eventually get to all its children and pass
// them catch responsibility through AddAsDependentForAllPrerequisites,
// although that'll be done lazily (only once they resolve/reject, so there
// is a possibility the DCHECKs might be racy.
for (AdjacencyListNode& node : prerequisites_->prerequisite_list) { for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
node.dependent_node.dependent = this; node.dependent_node.dependent = this;
...@@ -201,6 +196,20 @@ void AbstractPromise::MaybeInheritChecks(AbstractPromise* prerequisite) { ...@@ -201,6 +196,20 @@ void AbstractPromise::MaybeInheritChecks(AbstractPromise* prerequisite) {
prerequisite->passed_catch_responsibility_ = true; prerequisite->passed_catch_responsibility_ = true;
} }
void AbstractPromise::PassCatchResponsibilityOntoDependentsForCurriedPromise(
DependentList::Node* dependent_list) {
CheckedAutoLock lock(GetCheckedLock());
if (!dependent_list)
return;
if (IsResolvedWithPromise()) {
for (DependentList::Node* node = dependent_list; node;
node = node->next.load(std::memory_order_relaxed)) {
node->dependent->MaybeInheritChecks(this);
}
}
}
AbstractPromise::LocationRef::LocationRef(const Location& from_here) AbstractPromise::LocationRef::LocationRef(const Location& from_here)
: from_here_(from_here) {} : from_here_(from_here) {}
...@@ -344,6 +353,10 @@ void AbstractPromise::OnResolvePostReadyDependents() { ...@@ -344,6 +353,10 @@ void AbstractPromise::OnResolvePostReadyDependents() {
DependentList::Node* dependent_list = dependents_.ConsumeOnceForResolve(); DependentList::Node* dependent_list = dependents_.ConsumeOnceForResolve();
dependent_list = NonThreadSafeReverseList(dependent_list); dependent_list = NonThreadSafeReverseList(dependent_list);
#if DCHECK_IS_ON()
PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
#endif
// Propagate resolve to dependents. // Propagate resolve to dependents.
DependentList::Node* next; DependentList::Node* next;
for (DependentList::Node* node = dependent_list; node; node = next) { for (DependentList::Node* node = dependent_list; node; node = next) {
...@@ -361,6 +374,10 @@ void AbstractPromise::OnRejectPostReadyDependents() { ...@@ -361,6 +374,10 @@ void AbstractPromise::OnRejectPostReadyDependents() {
DependentList::Node* dependent_list = dependents_.ConsumeOnceForReject(); DependentList::Node* dependent_list = dependents_.ConsumeOnceForReject();
dependent_list = NonThreadSafeReverseList(dependent_list); dependent_list = NonThreadSafeReverseList(dependent_list);
#if DCHECK_IS_ON()
PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
#endif
// Propagate rejection to dependents. We always propagate rejection // Propagate rejection to dependents. We always propagate rejection
// immediately. // immediately.
DependentList::Node* next; DependentList::Node* next;
...@@ -412,8 +429,7 @@ void AbstractPromise::OnCanceled() { ...@@ -412,8 +429,7 @@ void AbstractPromise::OnCanceled() {
void AbstractPromise::OnResolved() { void AbstractPromise::OnResolved() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(executor_can_resolve_ || IsResolvedWithPromise()) DCHECK(executor_can_resolve_) << from_here_.ToString();
<< from_here_.ToString();
#endif #endif
if (IsResolvedWithPromise()) { if (IsResolvedWithPromise()) {
scoped_refptr<AbstractPromise> curried_promise = scoped_refptr<AbstractPromise> curried_promise =
......
...@@ -46,7 +46,7 @@ enum class RejectPolicy { ...@@ -46,7 +46,7 @@ enum class RejectPolicy {
// resolved. This lets us disambiguate promises with the same resolve and reject // resolved. This lets us disambiguate promises with the same resolve and reject
// type. // type.
template <typename T> template <typename T>
struct Resolved { struct BASE_EXPORT Resolved {
using Type = T; using Type = T;
static_assert(!std::is_same<T, NoReject>::value, static_assert(!std::is_same<T, NoReject>::value,
...@@ -64,7 +64,7 @@ struct Resolved { ...@@ -64,7 +64,7 @@ struct Resolved {
}; };
template <> template <>
struct Resolved<void> { struct BASE_EXPORT Resolved<void> {
using Type = void; using Type = void;
Void value; Void value;
}; };
...@@ -73,7 +73,7 @@ struct Resolved<void> { ...@@ -73,7 +73,7 @@ struct Resolved<void> {
// rejected. This lets us disambiguate promises with the same resolve and reject // rejected. This lets us disambiguate promises with the same resolve and reject
// type. // type.
template <typename T> template <typename T>
struct Rejected { struct BASE_EXPORT Rejected {
using Type = T; using Type = T;
T value; T value;
...@@ -93,7 +93,7 @@ struct Rejected { ...@@ -93,7 +93,7 @@ struct Rejected {
}; };
template <> template <>
struct Rejected<void> { struct BASE_EXPORT Rejected<void> {
using Type = void; using Type = void;
Void value; Void value;
}; };
...@@ -399,6 +399,10 @@ class BASE_EXPORT AbstractPromise ...@@ -399,6 +399,10 @@ class BASE_EXPORT AbstractPromise
void MaybeInheritChecks(AbstractPromise* source) void MaybeInheritChecks(AbstractPromise* source)
EXCLUSIVE_LOCKS_REQUIRED(GetCheckedLock()); EXCLUSIVE_LOCKS_REQUIRED(GetCheckedLock());
// Does nothing if this promise wasn't resolved by a promise.
void PassCatchResponsibilityOntoDependentsForCurriedPromise(
DependentList::Node* dependent_list);
// Controls how we deal with unhandled rejection. // Controls how we deal with unhandled rejection.
const RejectPolicy reject_policy_; const RejectPolicy reject_policy_;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TASK_PROMISE_PROMISE_RESULT_H_
#define BASE_TASK_PROMISE_PROMISE_RESULT_H_
#include <type_traits>
#include "base/containers/unique_any.h"
namespace base {
// An optional return type from a promise callback, which allows the callback to
// decide whether or not it should reject. E.g.
//
// enum class Error { kReason };
//
// PromiseResult<int, Error> MyFn() {
// int result;
// ...
// if (error)
// return Error::kReason;
// return result;
// }
//
// If ResolveType and RejectType are distinct PromiseResult's constructor will
// accept them. E.g.
//
// PromiseResult<int, std::string> pr(123); // Resolve
// PromiseResult<int, std::string> pr("whoops"); // Reject
//
// If ResolveType and RejectType are the same you need to use Resolved<> and
// Rejected<> to disambiguate. E.g.
//
// PromiseResult<int, int> pr(Resolved{123}); // Resolve
// PromiseResult<int, int> pr(Rejected{123}); // Reject
//
// PromiseResult<ResolveType, RejectType> has a constructor that accepts
// Promise<ResolveType, RejectType>.
template <typename ResolveType, typename RejectType = NoReject>
class PromiseResult {
public:
template <typename ResolveT = ResolveType,
typename RejectT = RejectType,
class Enable = std::enable_if<std::is_void<ResolveT>::value ^
std::is_void<RejectT>::value>>
PromiseResult() : PromiseResult(typename Analyze<void>::TagType()) {}
template <typename T>
PromiseResult(T&& t)
: PromiseResult(typename Analyze<std::decay_t<T>>::TagType(),
std::forward<T>(t)) {}
PromiseResult(PromiseResult&& other) noexcept = default;
PromiseResult(const PromiseResult&) = delete;
PromiseResult& operator=(const PromiseResult&) = delete;
unique_any& value() { return value_; }
private:
struct IsWrapped {};
struct IsResolved {};
struct IsRejected {};
struct IsPromise {};
// Helper that assigns one of the above tags for |T|.
template <typename T>
struct Analyze {
using DecayedT = std::decay_t<T>;
static constexpr bool is_resolve =
std::is_convertible<DecayedT, std::decay_t<ResolveType>>::value;
static constexpr bool is_reject =
std::is_convertible<DecayedT, std::decay_t<RejectType>>::value;
static_assert(!is_reject || !std::is_same<DecayedT, NoReject>::value,
"A NoReject promise can't reject");
static_assert(!std::is_same<std::decay_t<ResolveType>,
std::decay_t<RejectType>>::value,
"Ambiguous because ResolveType and RejectType are the same");
static_assert(is_resolve || is_reject,
"Argument matches neither resolve nor reject type.");
static_assert(
is_resolve != is_reject && (is_resolve || is_reject),
"Ambiguous because argument matches both ResolveType and RejectType");
using TagType = std::conditional_t<is_resolve, IsResolved, IsRejected>;
};
template <typename T>
struct Analyze<Resolved<T>> {
using TagType = IsWrapped;
static_assert(std::is_same<ResolveType, T>::value,
"T in Resolved<T> is not ResolveType");
};
template <typename T>
struct Analyze<Rejected<T>> {
static_assert(std::is_same<RejectType, T>::value,
"T in Rejected<T> is not RejectType");
static_assert(!std::is_same<RejectType, NoReject>::value,
"A NoReject promise can't reject");
using TagType = IsWrapped;
};
template <typename ResolveT, typename RejectT>
struct Analyze<Promise<ResolveT, RejectT>> {
using TagType = IsPromise;
static_assert(std::is_same<ResolveT, ResolveType>::value,
"Promise resolve types don't match");
static_assert(std::is_same<RejectT, RejectType>::value,
"Promise reject types don't match");
};
template <typename... Args>
PromiseResult(IsResolved, Args&&... args)
: value_(in_place_type_t<Resolved<ResolveType>>(),
std::forward<Args>(args)...) {}
template <typename... Args>
PromiseResult(IsRejected, Args&&... args)
: value_(in_place_type_t<Rejected<RejectType>>(),
std::forward<Args>(args)...) {}
PromiseResult(IsPromise, const Promise<ResolveType, RejectType>& promise)
: value_(promise.abstract_promise_) {}
template <typename T>
PromiseResult(IsWrapped, T&& t) : value_(std::forward<T>(t)) {}
unique_any value_;
};
} // namespace base
#endif // BASE_TASK_PROMISE_PROMISE_RESULT_H_
This diff is collapsed.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This is a "No Compile Test" suite.
// http://dev.chromium.org/developers/testing/no-compile-tests
#include "base/task/promise/promise.h"
#include "base/task/promise/promise_result.h"
namespace base {
#if defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
void WontCompile() {
Promise<int> p;
p.CatchOnCurrent(FROM_HERE, BindOnce([]() {}));
}
#elif defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE_TYPE_TWO) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
void WontCompile() {
Promise<int> p;
p.ThenOnCurrent(FROM_HERE, BindOnce([](int) {}), BindOnce([]() {}));
}
#elif defined(NCTEST_METHOD_RESOLVE_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_resolve| callback must accept Promise::ResolveType or void\."]
void WontCompile() {
Promise<int, void> p;
p.ThenOnCurrent(FROM_HERE, BindOnce([](bool) { }));
}
#elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
void WontCompile() {
Promise<int, void> p;
p.CatchOnCurrent(FROM_HERE, BindOnce([](bool) { }));
}
#elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH_TYPE_TWO) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
void WontCompile() {
Promise<int, void> p;
p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { }), BindOnce([](bool) { }));
}
#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
void WontCompile() {
Promise<void> p;
p.ThenOnCurrent(
FROM_HERE,
BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
BindOnce([](int err) -> Rejected<bool> { return "123"; }));
}
#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES2) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
void WontCompile() {
Promise<void> p;
p.ThenOnCurrent(
FROM_HERE,
BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
BindOnce([](int err) -> Resolved<std::string> { return "123"; }));
}
#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES3) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
void WontCompile() {
Promise<int, void> p;
p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { return true; }),
BindOnce([](int) { return 123.0; }));
}
#elif defined(NCTEST_METHOD_AMBIGUOUS_CONSTRUCTOR) // [r"fatal error: static_assert failed .*\"Ambiguous because ResolveType and RejectType are the same"]
void WontCompile() {
PromiseResult<void, void> pr;
}
#elif defined(NCTEST_METHOD_AMBIGUOUS_CONSTRUCTOR2) // [r"fatal error: static_assert failed .*\"Ambiguous because ResolveType and RejectType are the same"]
void WontCompile() {
PromiseResult<int, int> pr(123);
}
#elif defined(NCTEST_METHOD_REJECTED_NOREJECT) // [r"fatal error: static_assert failed .*\"Can't have Rejected<NoReject>"]
void WontCompile() {
Rejected<NoReject>();
}
#elif defined(NCTEST_METHOD_ARGUMENT_DOESNT_MATCH) // [r"fatal error: static_assert failed .*\"Argument matches neither resolve nor reject type\."]
void WontCompile() {
PromiseResult<int, float> pr("invalid");
}
#elif defined(NCTEST_METHOD_ARGUMENT_DOESNT_MATCH2) // [r"fatal error: static_assert failed .*\"Promise resolve types don't match"]
void WontCompile() {
Promise<void> p;
PromiseResult<int, float> pr(p);
}
#elif defined(NCTEST_METHOD_UNRELATED_RESOLVE) // [r"fatal error: static_assert failed .*\"T in Resolved<T> is not ResolveType"]
void WontCompile() {
struct Unrelated{};
PromiseResult<int, void> pr(Resolved<Unrelated>{});
}
#elif defined(NCTEST_METHOD_UNRELATED_REJECT) // [r"fatal error: static_assert failed .*\"T in Rejected<T> is not RejectType"]
void WontCompile() {
struct Unrelated{};
PromiseResult<int, void> pr(Rejected<Unrelated>{});
}
#elif defined(NCTEST_METHOD_AMBIGUOUS_REJECT_TYPE) // [r"fatal error: static_assert failed .*\"Ambiguous promise reject type"]
void WontCompile() {
Promise<int, void> p1;
// If supported |p2| would have type Promise<NoReject, variant<void, bool>>.
auto p2 = p1.ThenOnCurrent(FROM_HERE, BindOnce([]() { return Rejected<bool>(true); }));
}
#elif defined(NCTEST_METHOD_AMBIGUOUS_RESOLVE_TYPE) // [r"fatal error: static_assert failed .*\"Ambiguous promise resolve type"]
void WontCompile() {
Promise<int, void> p1;
// If supported |p2| would have type Promise<variant<int, bool>, NoReject>.
auto p2 = p1.CatchOnCurrent(FROM_HERE, BindOnce([](int) { return Resolved<bool>(true); }));
}
#endif
} // namespace base
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/task/promise/then_and_catch_executor.h"
namespace base {
namespace internal {
BaseThenAndCatchExecutor::BaseThenAndCatchExecutor(
internal::CallbackBase&& resolve_executor,
internal::CallbackBase&& reject_executor)
: resolve_callback_(std::move(resolve_executor)),
reject_callback_(std::move(reject_executor)) {}
BaseThenAndCatchExecutor::~BaseThenAndCatchExecutor() = default;
bool BaseThenAndCatchExecutor::IsCancelled() const {
if (!resolve_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();
}
return reject_callback_.IsCancelled();
}
AbstractPromise::Executor::PrerequisitePolicy
BaseThenAndCatchExecutor::GetPrerequisitePolicy() {
return PrerequisitePolicy::kAll;
}
void BaseThenAndCatchExecutor::Execute(AbstractPromise* promise) {
AbstractPromise* prerequisite =
promise->GetOnlyPrerequisite()->FindNonCurriedAncestor();
if (prerequisite->IsResolved()) {
if (ProcessNullCallback(resolve_callback_, prerequisite, promise)) {
promise->OnResolved();
return;
}
ExecuteThen(prerequisite, promise);
} else {
DCHECK(prerequisite->IsRejected());
if (ProcessNullCallback(reject_callback_, prerequisite, promise)) {
promise->OnResolved();
return;
}
ExecuteCatch(prerequisite, promise);
}
}
// static
bool BaseThenAndCatchExecutor::ProcessNullCallback(const CallbackBase& callback,
AbstractPromise* arg,
AbstractPromise* result) {
if (callback.is_null()) {
// A curried promise is used to forward the result through null callbacks.
result->emplace(scoped_refptr<AbstractPromise>(arg));
DCHECK(result->IsResolvedWithPromise());
return true;
}
return false;
}
} // namespace internal
} // namespace base
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
#define BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
#include <type_traits>
#include "base/callback.h"
#include "base/task/promise/abstract_promise.h"
#include "base/task/promise/helpers.h"
namespace base {
namespace internal {
// Exists to reduce template bloat.
class BASE_EXPORT BaseThenAndCatchExecutor : public AbstractPromise::Executor {
public:
BaseThenAndCatchExecutor(CallbackBase&& resolve_callback,
CallbackBase&& reject_callback);
~BaseThenAndCatchExecutor() override;
// AbstractPromise::Executor:
bool IsCancelled() const override;
PrerequisitePolicy GetPrerequisitePolicy() override;
void Execute(AbstractPromise* promise) override;
protected:
virtual void ExecuteThen(AbstractPromise* prerequisite,
AbstractPromise* promise) = 0;
virtual void ExecuteCatch(AbstractPromise* prerequisite,
AbstractPromise* promise) = 0;
// If |executor| is null then the value of |arg| is moved or copied into
// |result| and true is returned. Otherwise false is returned.
static bool ProcessNullCallback(const CallbackBase& executor,
AbstractPromise* arg,
AbstractPromise* result);
CallbackBase resolve_callback_;
CallbackBase reject_callback_;
};
// Tag signals no callback which is used to eliminate dead code.
struct NoCallback {};
struct CouldResolveOrReject {};
struct CanOnlyResolve {};
struct CanOnlyReject {};
template <bool can_resolve, bool can_reject>
struct CheckResultHelper;
template <>
struct CheckResultHelper<true, false> {
using TagType = CanOnlyResolve;
};
template <>
struct CheckResultHelper<true, true> {
using TagType = CouldResolveOrReject;
};
template <>
struct CheckResultHelper<false, true> {
using TagType = CanOnlyReject;
};
template <typename ResolveOnceCallback,
typename RejectOnceCallback,
typename ArgResolve,
typename ArgReject,
typename ResolveStorage,
typename RejectStorage>
class ThenAndCatchExecutor : public BaseThenAndCatchExecutor {
public:
using ResolveReturnT =
typename CallbackTraits<ResolveOnceCallback>::ReturnType;
using RejectReturnT = typename CallbackTraits<RejectOnceCallback>::ReturnType;
using PrerequisiteCouldResolve =
std::integral_constant<bool,
!std::is_same<ArgResolve, NoCallback>::value>;
using PrerequisiteCouldReject =
std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>;
ThenAndCatchExecutor(ResolveOnceCallback&& resolve_callback,
RejectOnceCallback&& reject_callback)
: BaseThenAndCatchExecutor(std::move(resolve_callback),
std::move(reject_callback)) {
static_assert(sizeof(CallbackBase) == sizeof(ResolveOnceCallback),
"We assume it's possible to cast from CallbackBase to "
"ResolveOnceCallback");
static_assert(sizeof(CallbackBase) == sizeof(RejectOnceCallback),
"We assume it's possible to cast from CallbackBase to "
"RejectOnceCallback");
}
~ThenAndCatchExecutor() override = default;
void ExecuteThen(AbstractPromise* prerequisite,
AbstractPromise* promise) override {
ExecuteThenInternal(prerequisite, promise, PrerequisiteCouldResolve());
}
void ExecuteCatch(AbstractPromise* prerequisite,
AbstractPromise* promise) override {
ExecuteCatchInternal(prerequisite, promise, PrerequisiteCouldReject());
}
#if DCHECK_IS_ON()
ArgumentPassingType ResolveArgumentPassingType() const override {
return resolve_callback_.is_null()
? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
: CallbackTraits<ResolveOnceCallback>::argument_passing_type;
}
ArgumentPassingType RejectArgumentPassingType() const override {
return reject_callback_.is_null()
? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
: CallbackTraits<RejectOnceCallback>::argument_passing_type;
}
bool CanResolve() const override {
return (!resolve_callback_.is_null() &&
PromiseCallbackTraits<ResolveReturnT>::could_resolve) ||
(!reject_callback_.is_null() &&
PromiseCallbackTraits<RejectReturnT>::could_resolve);
}
bool CanReject() const override {
return (!resolve_callback_.is_null() &&
PromiseCallbackTraits<ResolveReturnT>::could_reject) ||
(!reject_callback_.is_null() &&
PromiseCallbackTraits<RejectReturnT>::could_reject);
}
#endif
private:
void ExecuteThenInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
std::true_type can_resolve) {
ResolveOnceCallback* resolve_callback =
static_cast<ResolveOnceCallback*>(&resolve_callback_);
RunHelper<ResolveOnceCallback, Resolved<ArgResolve>, ResolveStorage,
RejectStorage>::Run(std::move(*resolve_callback), prerequisite,
promise);
using CheckResultTagType = typename CheckResultHelper<
PromiseCallbackTraits<ResolveReturnT>::could_resolve,
PromiseCallbackTraits<ResolveReturnT>::could_reject>::TagType;
CheckResultType(promise, CheckResultTagType());
}
void ExecuteThenInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
std::false_type can_resolve) {
// |prerequisite| can't resolve so don't generate dead code.
}
void ExecuteCatchInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
std::true_type can_reject) {
RejectOnceCallback* reject_callback =
static_cast<RejectOnceCallback*>(&reject_callback_);
RunHelper<RejectOnceCallback, Rejected<ArgReject>, ResolveStorage,
RejectStorage>::Run(std::move(*reject_callback), prerequisite,
promise);
using CheckResultTagType = typename CheckResultHelper<
PromiseCallbackTraits<RejectReturnT>::could_resolve,
PromiseCallbackTraits<RejectReturnT>::could_reject>::TagType;
CheckResultType(promise, CheckResultTagType());
}
void ExecuteCatchInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
std::false_type can_reject) {
// |prerequisite| can't reject so don't generate dead code.
}
void CheckResultType(AbstractPromise* promise, CouldResolveOrReject) {
if (promise->IsResolvedWithPromise() ||
promise->value().type() == TypeId::From<ResolveStorage>()) {
promise->OnResolved();
} else {
DCHECK_EQ(promise->value().type(), TypeId::From<RejectStorage>())
<< " See " << promise->from_here().ToString();
promise->OnRejected();
}
}
void CheckResultType(AbstractPromise* promise, CanOnlyResolve) {
promise->OnResolved();
}
void CheckResultType(AbstractPromise* promise, CanOnlyReject) {
promise->OnRejected();
}
};
} // namespace internal
} // namespace base
#endif // BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
...@@ -40,40 +40,8 @@ bool PostTaskAndReplyTaskRunner::PostTask(const Location& from_here, ...@@ -40,40 +40,8 @@ bool PostTaskAndReplyTaskRunner::PostTask(const Location& from_here,
return destination_->PostTask(from_here, std::move(task)); return destination_->PostTask(from_here, std::move(task));
} }
// TODO(alexclarke): Remove this when TaskRunner::PostPromiseInternal becomes
// pure virtual.
class PromiseHolder {
public:
explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise)
: promise_(std::move(promise)) {}
~PromiseHolder() {
// Detect if the promise was not executed and if so cancel to ensure memory
// is released.
if (promise_)
promise_->OnCanceled();
}
PromiseHolder(PromiseHolder&& other) : promise_(std::move(other.promise_)) {}
scoped_refptr<internal::AbstractPromise> Unwrap() const {
return std::move(promise_);
}
private:
mutable scoped_refptr<internal::AbstractPromise> promise_;
};
} // namespace } // namespace
template <>
struct BindUnwrapTraits<PromiseHolder> {
static scoped_refptr<internal::AbstractPromise> Unwrap(
const PromiseHolder& o) {
return o.Unwrap();
}
};
bool TaskRunner::PostTask(const Location& from_here, OnceClosure task) { bool TaskRunner::PostTask(const Location& from_here, OnceClosure task) {
return PostDelayedTask(from_here, std::move(task), base::TimeDelta()); return PostDelayedTask(from_here, std::move(task), base::TimeDelta());
} }
...@@ -90,8 +58,7 @@ bool TaskRunner::PostPromiseInternal( ...@@ -90,8 +58,7 @@ bool TaskRunner::PostPromiseInternal(
base::TimeDelta delay) { base::TimeDelta delay) {
return PostDelayedTask( return PostDelayedTask(
promise->from_here(), promise->from_here(),
BindOnce(&internal::AbstractPromise::Execute, PromiseHolder(promise)), BindOnce(&internal::AbstractPromise::Execute, std::move(promise)), delay);
delay);
} }
TaskRunner::TaskRunner() = default; TaskRunner::TaskRunner() = default;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment