forked from mirrors/gecko-dev
Bug 1917242 - Destroy callbacks before chaining completion promise; r=xpcom-reviewers,nika a=RyanVM
Differential Revision: https://phabricator.services.mozilla.com/D221321
This commit is contained in:
parent
cdefc8416d
commit
80194fbe0b
2 changed files with 62 additions and 59 deletions
|
|
@ -817,7 +817,7 @@ TEST(MozPromise, MapErr)
|
|||
EXPECT_EQ(ran_ok, false);
|
||||
}
|
||||
|
||||
TEST(MozPromise, DISABLED_ObjectDestructionOrder)
|
||||
TEST(MozPromise, ObjectDestructionOrder)
|
||||
{
|
||||
AutoTaskQueue atq;
|
||||
RefPtr<TaskQueue> queue = atq.Queue();
|
||||
|
|
|
|||
|
|
@ -642,45 +642,41 @@ class MozPromise : public MozPromiseBase {
|
|||
};
|
||||
|
||||
/*
|
||||
* We create two overloads for invoking Resolve/Reject Methods so as to
|
||||
* make the resolve/reject value argument "optional".
|
||||
* Helper to make the resolve/reject value argument "optional".
|
||||
*/
|
||||
template <typename ThisType, typename MethodType, typename ValueType>
|
||||
static std::enable_if_t<TakesAnyArguments<MethodType>,
|
||||
MethodReturnType<MethodType>>
|
||||
InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
|
||||
static MethodReturnType<MethodType> InvokeMethod(ThisType* aThisVal,
|
||||
MethodType aMethod,
|
||||
ValueType&& aValue) {
|
||||
if constexpr (TakesAnyArguments<MethodType>) {
|
||||
return (aThisVal->*aMethod)(std::forward<ValueType>(aValue));
|
||||
}
|
||||
|
||||
template <typename ThisType, typename MethodType, typename ValueType>
|
||||
static std::enable_if_t<!TakesAnyArguments<MethodType>,
|
||||
MethodReturnType<MethodType>>
|
||||
InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
|
||||
} else {
|
||||
return (aThisVal->*aMethod)();
|
||||
}
|
||||
|
||||
// Called when promise chaining is supported.
|
||||
template <bool SupportChaining, typename ThisType, typename MethodType,
|
||||
typename ValueType, typename CompletionPromiseType>
|
||||
static std::enable_if_t<SupportChaining, void> InvokeCallbackMethod(
|
||||
ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
|
||||
CompletionPromiseType&& aCompletionPromise) {
|
||||
auto p = InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
|
||||
if (aCompletionPromise) {
|
||||
p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
|
||||
}
|
||||
}
|
||||
|
||||
// Called when promise chaining is not supported.
|
||||
template <bool SupportChaining, typename ThisType, typename MethodType,
|
||||
typename ValueType, typename CompletionPromiseType>
|
||||
static std::enable_if_t<!SupportChaining, void> InvokeCallbackMethod(
|
||||
ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
|
||||
CompletionPromiseType&& aCompletionPromise) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(
|
||||
!aCompletionPromise,
|
||||
"Can't do promise chaining for a non-promise-returning method.");
|
||||
template <bool SupportChaining, typename PromiseType, typename ThisType,
|
||||
typename MethodType, typename ValueType>
|
||||
static RefPtr<PromiseType> InvokeCallbackMethod(ThisType* aThisVal,
|
||||
MethodType aMethod,
|
||||
ValueType&& aValue) {
|
||||
if constexpr (SupportChaining) {
|
||||
return InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
|
||||
} else {
|
||||
InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PromiseType>
|
||||
static void MaybeChain(PromiseType* aFrom,
|
||||
RefPtr<typename PromiseType::Private>&& aTo) {
|
||||
if (aTo) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(
|
||||
aFrom,
|
||||
"Can't do promise chaining for a non-promise-returning method.");
|
||||
aFrom->ChainTo(aTo.forget(), "<chained completion promise>");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename>
|
||||
|
|
@ -729,21 +725,22 @@ class MozPromise : public MozPromiseBase {
|
|||
}
|
||||
|
||||
void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
|
||||
if (aValue.IsResolve()) {
|
||||
InvokeCallbackMethod<SupportChaining>(mThisVal.get(), mResolveMethod,
|
||||
MaybeMove(aValue.ResolveValue()),
|
||||
std::move(mCompletionPromise));
|
||||
} else {
|
||||
InvokeCallbackMethod<SupportChaining>(mThisVal.get(), mRejectMethod,
|
||||
MaybeMove(aValue.RejectValue()),
|
||||
std::move(mCompletionPromise));
|
||||
}
|
||||
RefPtr<PromiseType> result =
|
||||
aValue.IsResolve()
|
||||
? InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mThisVal.get(), mResolveMethod,
|
||||
MaybeMove(aValue.ResolveValue()))
|
||||
: InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mThisVal.get(), mRejectMethod,
|
||||
MaybeMove(aValue.RejectValue()));
|
||||
|
||||
// Null out mThisVal after invoking the callback so that any references
|
||||
// are released predictably on the dispatch thread. Otherwise, it would be
|
||||
// released on whatever thread last drops its reference to the ThenValue,
|
||||
// which may or may not be ok.
|
||||
mThisVal = nullptr;
|
||||
|
||||
MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -789,15 +786,17 @@ class MozPromise : public MozPromiseBase {
|
|||
}
|
||||
|
||||
void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
|
||||
InvokeCallbackMethod<SupportChaining>(
|
||||
mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue),
|
||||
std::move(mCompletionPromise));
|
||||
RefPtr<PromiseType> result =
|
||||
InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue));
|
||||
|
||||
// Null out mThisVal after invoking the callback so that any references
|
||||
// are released predictably on the dispatch thread. Otherwise, it would be
|
||||
// released on whatever thread last drops its reference to the ThenValue,
|
||||
// which may or may not be ok.
|
||||
mThisVal = nullptr;
|
||||
|
||||
MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -853,15 +852,14 @@ class MozPromise : public MozPromiseBase {
|
|||
// classes with ::operator()), since it allows us to share code more
|
||||
// easily. We could fix this if need be, though it's quite easy to work
|
||||
// around by just capturing something.
|
||||
if (aValue.IsResolve()) {
|
||||
InvokeCallbackMethod<SupportChaining>(
|
||||
RefPtr<PromiseType> result =
|
||||
aValue.IsResolve()
|
||||
? InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mResolveFunction.ptr(), &ResolveFunction::operator(),
|
||||
MaybeMove(aValue.ResolveValue()), std::move(mCompletionPromise));
|
||||
} else {
|
||||
InvokeCallbackMethod<SupportChaining>(
|
||||
MaybeMove(aValue.ResolveValue()))
|
||||
: InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mRejectFunction.ptr(), &RejectFunction::operator(),
|
||||
MaybeMove(aValue.RejectValue()), std::move(mCompletionPromise));
|
||||
}
|
||||
MaybeMove(aValue.RejectValue()));
|
||||
|
||||
// Destroy callbacks after invocation so that any references in closures
|
||||
// are released predictably on the dispatch thread. Otherwise, they would
|
||||
|
|
@ -869,6 +867,8 @@ class MozPromise : public MozPromiseBase {
|
|||
// ThenValue, which may or may not be ok.
|
||||
mResolveFunction.reset();
|
||||
mRejectFunction.reset();
|
||||
|
||||
MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -919,15 +919,18 @@ class MozPromise : public MozPromiseBase {
|
|||
// classes with ::operator()), since it allows us to share code more
|
||||
// easily. We could fix this if need be, though it's quite easy to work
|
||||
// around by just capturing something.
|
||||
InvokeCallbackMethod<SupportChaining>(
|
||||
RefPtr<PromiseType> result =
|
||||
InvokeCallbackMethod<SupportChaining, PromiseType>(
|
||||
mResolveRejectFunction.ptr(), &ResolveRejectFunction::operator(),
|
||||
MaybeMove(aValue), std::move(mCompletionPromise));
|
||||
MaybeMove(aValue));
|
||||
|
||||
// Destroy callbacks after invocation so that any references in closures
|
||||
// are released predictably on the dispatch thread. Otherwise, they would
|
||||
// be released on whatever thread last drops its reference to the
|
||||
// ThenValue, which may or may not be ok.
|
||||
mResolveRejectFunction.reset();
|
||||
|
||||
MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
Loading…
Reference in a new issue