1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #if !defined(MozPromise_h_)
10 # include <type_traits>
13 # include "mozilla/Logging.h"
14 # include "mozilla/Maybe.h"
15 # include "mozilla/Monitor.h"
16 # include "mozilla/Mutex.h"
17 # include "mozilla/RefPtr.h"
18 # include "mozilla/Tuple.h"
19 # include "mozilla/UniquePtr.h"
20 # include "mozilla/Variant.h"
21 # include "nsIDirectTaskDispatcher.h"
22 # include "nsISerialEventTarget.h"
23 # include "nsTArray.h"
24 # include "nsThreadUtils.h"
26 # ifdef MOZ_WIDGET_ANDROID
27 # include "mozilla/jni/GeckoResultUtils.h"
30 # if MOZ_DIAGNOSTIC_ASSERT_ENABLED
31 # define PROMISE_DEBUG
35 # define PROMISE_ASSERT MOZ_RELEASE_ASSERT
37 # define PROMISE_ASSERT(...) \
43 # include "nsPrintfCString.h"
52 extern LazyLogModule gMozPromiseLog
;
54 # define PROMISE_LOG(x, ...) \
55 MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
59 struct MethodTraitsHelper
: MethodTraitsHelper
<decltype(&F::operator())> {};
60 template <typename ThisType
, typename Ret
, typename
... ArgTypes
>
61 struct MethodTraitsHelper
<Ret (ThisType::*)(ArgTypes
...)> {
62 using ReturnType
= Ret
;
63 static const size_t ArgSize
= sizeof...(ArgTypes
);
65 template <typename ThisType
, typename Ret
, typename
... ArgTypes
>
66 struct MethodTraitsHelper
<Ret (ThisType::*)(ArgTypes
...) const> {
67 using ReturnType
= Ret
;
68 static const size_t ArgSize
= sizeof...(ArgTypes
);
70 template <typename ThisType
, typename Ret
, typename
... ArgTypes
>
71 struct MethodTraitsHelper
<Ret (ThisType::*)(ArgTypes
...) volatile> {
72 using ReturnType
= Ret
;
73 static const size_t ArgSize
= sizeof...(ArgTypes
);
75 template <typename ThisType
, typename Ret
, typename
... ArgTypes
>
76 struct MethodTraitsHelper
<Ret (ThisType::*)(ArgTypes
...) const volatile> {
77 using ReturnType
= Ret
;
78 static const size_t ArgSize
= sizeof...(ArgTypes
);
81 struct MethodTrait
: MethodTraitsHelper
<std::remove_reference_t
<T
>> {};
85 template <typename MethodType
>
87 std::integral_constant
<bool, detail::MethodTrait
<MethodType
>::ArgSize
!= 0>;
89 template <typename MethodType
, typename TargetType
>
91 std::is_convertible
<typename
detail::MethodTrait
<MethodType
>::ReturnType
,
94 template <typename ResolveValueT
, typename RejectValueT
, bool IsExclusive
>
97 template <typename Return
>
98 struct IsMozPromise
: std::false_type
{};
100 template <typename ResolveValueT
, typename RejectValueT
, bool IsExclusive
>
101 struct IsMozPromise
<MozPromise
<ResolveValueT
, RejectValueT
, IsExclusive
>>
105 * A promise manages an asynchronous request that may or may not be able to be
106 * fulfilled immediately. When an API returns a promise, the consumer may attach
107 * callbacks to be invoked (asynchronously, on a specified thread) when the
108 * request is either completed (resolved) or cannot be completed (rejected).
109 * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
110 * MozPromises resolution/rejection make a normal round-trip through the event
111 * loop, which simplifies their ordering semantics relative to other native
114 * MozPromises attempt to mirror the spirit of JS Promises to the extent that
115 * is possible (and desirable) in C++. While the intent is that MozPromises
116 * feel familiar to programmers who are accustomed to their JS-implemented
117 * cousin, we don't shy away from imposing restrictions and adding features that
118 * make sense for the use cases we encounter.
120 * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
121 * call accepts resolve and reject callbacks, and returns a magic object which
122 * will be implicitly converted to a MozPromise::Request or a MozPromise object
123 * depending on how the return value is used. The magic object serves several
124 * purposes for the consumer.
126 * (1) When converting to a MozPromise::Request, it allows the caller to
127 * cancel the delivery of the resolve/reject value if it has not already
128 * occurred, via Disconnect() (this must be done on the target thread to
131 * (2) When converting to a MozPromise (which is called a completion promise),
132 * it allows promise chaining so ->Then() can be called again to attach
133 * more resolve and reject callbacks. If the resolve/reject callback
134 * returns a new MozPromise, that promise is chained to the completion
135 * promise, such that its resolve/reject value will be forwarded along
136 * when it arrives. If the resolve/reject callback returns void, the
137 * completion promise is resolved/rejected with the same value that was
138 * passed to the callback.
140 * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
141 * (rather than already_AddRefed) from various methods. This is done to allow
142 * elegant chaining of calls without cluttering up the code with intermediate
143 * variables, and without introducing separate API variants for callers that
144 * want a return value (from, say, ->Then()) from those that don't.
146 * When IsExclusive is true, the MozPromise does a release-mode assertion that
147 * there is at most one call to either Then(...) or ChainTo(...).
150 class MozPromiseRefcountable
{
152 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable
)
154 virtual ~MozPromiseRefcountable() = default;
157 class MozPromiseBase
: public MozPromiseRefcountable
{
159 virtual void AssertIsDead() = 0;
162 template <typename T
>
163 class MozPromiseHolder
;
164 template <typename T
>
165 class MozPromiseRequestHolder
;
166 template <typename ResolveValueT
, typename RejectValueT
, bool IsExclusive
>
167 class MozPromise
: public MozPromiseBase
{
168 static const uint32_t sMagic
= 0xcecace11;
170 // Return a |T&&| to enable move when IsExclusive is true or
171 // a |const T&| to enforce copy otherwise.
172 template <typename T
,
173 typename R
= std::conditional_t
<IsExclusive
, T
&&, const T
&>>
174 static R
MaybeMove(T
& aX
) {
175 return static_cast<R
>(aX
);
179 typedef ResolveValueT ResolveValueType
;
180 typedef RejectValueT RejectValueType
;
181 class ResolveOrRejectValue
{
183 template <typename ResolveValueType_
>
184 void SetResolve(ResolveValueType_
&& aResolveValue
) {
185 MOZ_ASSERT(IsNothing());
186 mValue
= Storage(VariantIndex
<ResolveIndex
>{},
187 std::forward
<ResolveValueType_
>(aResolveValue
));
190 template <typename RejectValueType_
>
191 void SetReject(RejectValueType_
&& aRejectValue
) {
192 MOZ_ASSERT(IsNothing());
193 mValue
= Storage(VariantIndex
<RejectIndex
>{},
194 std::forward
<RejectValueType_
>(aRejectValue
));
197 template <typename ResolveValueType_
>
198 static ResolveOrRejectValue
MakeResolve(ResolveValueType_
&& aResolveValue
) {
199 ResolveOrRejectValue val
;
200 val
.SetResolve(std::forward
<ResolveValueType_
>(aResolveValue
));
204 template <typename RejectValueType_
>
205 static ResolveOrRejectValue
MakeReject(RejectValueType_
&& aRejectValue
) {
206 ResolveOrRejectValue val
;
207 val
.SetReject(std::forward
<RejectValueType_
>(aRejectValue
));
211 bool IsResolve() const { return mValue
.template is
<ResolveIndex
>(); }
212 bool IsReject() const { return mValue
.template is
<RejectIndex
>(); }
213 bool IsNothing() const { return mValue
.template is
<NothingIndex
>(); }
215 const ResolveValueType
& ResolveValue() const {
216 return mValue
.template as
<ResolveIndex
>();
218 ResolveValueType
& ResolveValue() {
219 return mValue
.template as
<ResolveIndex
>();
221 const RejectValueType
& RejectValue() const {
222 return mValue
.template as
<RejectIndex
>();
224 RejectValueType
& RejectValue() { return mValue
.template as
<RejectIndex
>(); }
227 enum { NothingIndex
, ResolveIndex
, RejectIndex
};
228 using Storage
= Variant
<Nothing
, ResolveValueType
, RejectValueType
>;
229 Storage mValue
= Storage(VariantIndex
<NothingIndex
>{});
233 // MozPromise is the public type, and never constructed directly. Construct
234 // a MozPromise::Private, defined below.
235 MozPromise(const char* aCreationSite
, bool aIsCompletionPromise
)
236 : mCreationSite(aCreationSite
),
237 mMutex("MozPromise Mutex"),
239 mIsCompletionPromise(aIsCompletionPromise
)
240 # ifdef PROMISE_DEBUG
245 PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite
, this);
249 // MozPromise::Private allows us to separate the public interface (upon which
250 // consumers of the promise may invoke methods like Then()) from the private
251 // interface (upon which the creator of the promise may invoke Resolve() or
252 // Reject()). APIs should create and store a MozPromise::Private (usually
253 // via a MozPromiseHolder), and return a MozPromise to consumers.
255 // NB: We can include the definition of this class inline once B2G ICS is
259 template <typename ResolveValueType_
>
260 [[nodiscard
]] static RefPtr
<MozPromise
> CreateAndResolve(
261 ResolveValueType_
&& aResolveValue
, const char* aResolveSite
) {
262 static_assert(std::is_convertible_v
<ResolveValueType_
, ResolveValueT
>,
263 "Resolve() argument must be implicitly convertible to "
264 "MozPromise's ResolveValueT");
265 RefPtr
<typename
MozPromise::Private
> p
=
266 new MozPromise::Private(aResolveSite
);
267 p
->Resolve(std::forward
<ResolveValueType_
>(aResolveValue
), aResolveSite
);
271 template <typename RejectValueType_
>
272 [[nodiscard
]] static RefPtr
<MozPromise
> CreateAndReject(
273 RejectValueType_
&& aRejectValue
, const char* aRejectSite
) {
274 static_assert(std::is_convertible_v
<RejectValueType_
, RejectValueT
>,
275 "Reject() argument must be implicitly convertible to "
276 "MozPromise's RejectValueT");
277 RefPtr
<typename
MozPromise::Private
> p
=
278 new MozPromise::Private(aRejectSite
);
279 p
->Reject(std::forward
<RejectValueType_
>(aRejectValue
), aRejectSite
);
283 template <typename ResolveOrRejectValueType_
>
284 [[nodiscard
]] static RefPtr
<MozPromise
> CreateAndResolveOrReject(
285 ResolveOrRejectValueType_
&& aValue
, const char* aSite
) {
286 RefPtr
<typename
MozPromise::Private
> p
= new MozPromise::Private(aSite
);
287 p
->ResolveOrReject(std::forward
<ResolveOrRejectValueType_
>(aValue
), aSite
);
291 typedef MozPromise
<CopyableTArray
<ResolveValueType
>, RejectValueType
,
296 class AllPromiseHolder
: public MozPromiseRefcountable
{
298 explicit AllPromiseHolder(size_t aDependentPromises
)
299 : mPromise(new typename
AllPromiseType::Private(__func__
)),
300 mOutstandingPromises(aDependentPromises
) {
301 MOZ_ASSERT(aDependentPromises
> 0);
302 mResolveValues
.SetLength(aDependentPromises
);
305 void Resolve(size_t aIndex
, ResolveValueType
&& aResolveValue
) {
311 mResolveValues
[aIndex
].emplace(std::move(aResolveValue
));
312 if (--mOutstandingPromises
== 0) {
313 nsTArray
<ResolveValueType
> resolveValues
;
314 resolveValues
.SetCapacity(mResolveValues
.Length());
315 for (auto&& resolveValue
: mResolveValues
) {
316 resolveValues
.AppendElement(std::move(resolveValue
.ref()));
319 mPromise
->Resolve(std::move(resolveValues
), __func__
);
321 mResolveValues
.Clear();
325 void Reject(RejectValueType
&& aRejectValue
) {
331 mPromise
->Reject(std::move(aRejectValue
), __func__
);
333 mResolveValues
.Clear();
336 AllPromiseType
* Promise() { return mPromise
; }
339 nsTArray
<Maybe
<ResolveValueType
>> mResolveValues
;
340 RefPtr
<typename
AllPromiseType::Private
> mPromise
;
341 size_t mOutstandingPromises
;
345 [[nodiscard
]] static RefPtr
<AllPromiseType
> All(
346 nsISerialEventTarget
* aProcessingTarget
,
347 nsTArray
<RefPtr
<MozPromise
>>& aPromises
) {
348 if (aPromises
.Length() == 0) {
349 return AllPromiseType::CreateAndResolve(
350 CopyableTArray
<ResolveValueType
>(), __func__
);
353 RefPtr
<AllPromiseHolder
> holder
= new AllPromiseHolder(aPromises
.Length());
354 RefPtr
<AllPromiseType
> promise
= holder
->Promise();
355 for (size_t i
= 0; i
< aPromises
.Length(); ++i
) {
357 aProcessingTarget
, __func__
,
358 [holder
, i
](ResolveValueType aResolveValue
) -> void {
359 holder
->Resolve(i
, std::move(aResolveValue
));
361 [holder
](RejectValueType aRejectValue
) -> void {
362 holder
->Reject(std::move(aRejectValue
));
368 class Request
: public MozPromiseRefcountable
{
370 virtual void Disconnect() = 0;
373 Request() : mComplete(false), mDisconnected(false) {}
374 virtual ~Request() = default;
382 * A ThenValue tracks a single consumer waiting on the promise. When a
383 * consumer invokes promise->Then(...), a ThenValue is created. Once the
384 * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
385 * which invokes the resolve/reject method and then deletes the ThenValue.
387 class ThenValueBase
: public Request
{
388 friend class MozPromise
;
389 static const uint32_t sMagic
= 0xfadece11;
392 class ResolveOrRejectRunnable
: public CancelableRunnable
{
394 ResolveOrRejectRunnable(ThenValueBase
* aThenValue
, MozPromise
* aPromise
)
395 : CancelableRunnable(
396 "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
397 mThenValue(aThenValue
),
399 MOZ_DIAGNOSTIC_ASSERT(!mPromise
->IsPending());
402 ~ResolveOrRejectRunnable() {
404 mThenValue
->AssertIsDead();
408 NS_IMETHOD
Run() override
{
409 PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
410 mThenValue
->DoResolveOrReject(mPromise
->Value());
411 mThenValue
= nullptr;
416 nsresult
Cancel() override
{ return Run(); }
419 RefPtr
<ThenValueBase
> mThenValue
;
420 RefPtr
<MozPromise
> mPromise
;
423 ThenValueBase(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
)
424 : mResponseTarget(aResponseTarget
), mCallSite(aCallSite
) {
425 MOZ_ASSERT(aResponseTarget
);
428 # ifdef PROMISE_DEBUG
435 void AssertIsDead() {
436 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
437 // We want to assert that this ThenValues is dead - that is to say, that
438 // there are no consumers waiting for the result. In the case of a normal
439 // ThenValue, we check that it has been disconnected, which is the way
440 // that the consumer signals that it no longer wishes to hear about the
441 // result. If this ThenValue has a completion promise (which is mutually
442 // exclusive with being disconnectable), we recursively assert that every
443 // ThenValue associated with the completion promise is dead.
444 if (MozPromiseBase
* p
= CompletionPromise()) {
447 MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected
);
451 void Dispatch(MozPromise
* aPromise
) {
452 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
453 aPromise
->mMutex
.AssertCurrentThreadOwns();
454 MOZ_ASSERT(!aPromise
->IsPending());
456 nsCOMPtr
<nsIRunnable
> r
= new ResolveOrRejectRunnable(this, aPromise
);
458 "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
460 aPromise
->mValue
.IsResolve() ? "Resolving" : "Rejecting", mCallSite
,
461 r
.get(), aPromise
, this,
462 aPromise
->mUseSynchronousTaskDispatch
464 : aPromise
->mUseDirectTaskDispatch
? "directtask" : "normal");
466 if (aPromise
->mUseSynchronousTaskDispatch
&&
467 mResponseTarget
->IsOnCurrentThread()) {
468 PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
474 if (aPromise
->mUseDirectTaskDispatch
&&
475 mResponseTarget
->IsOnCurrentThread()) {
477 "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
479 nsCOMPtr
<nsIDirectTaskDispatcher
> dispatcher
=
480 do_QueryInterface(mResponseTarget
);
482 dispatcher
->DispatchDirectTask(r
.forget());
487 "Direct Task dispatching not available for thread \"%s\"",
488 PR_GetThreadName(PR_GetCurrentThread()))
490 MOZ_DIAGNOSTIC_ASSERT(
492 "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
496 // Promise consumers are allowed to disconnect the Request object and
497 // then shut down the thread or task queue that the promise result would
498 // be dispatched on. So we unfortunately can't assert that promise
499 // dispatch succeeds. :-(
500 mResponseTarget
->Dispatch(r
.forget());
503 void Disconnect() override
{
504 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget
->IsOnCurrentThread());
505 MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete
);
506 Request::mDisconnected
= true;
508 // We could support rejecting the completion promise on disconnection, but
509 // then we'd need to have some sort of default reject value. The use cases
510 // of disconnection and completion promise chaining seem pretty
511 // orthogonal, so let's use assert against it.
512 MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
516 virtual MozPromiseBase
* CompletionPromise() const = 0;
517 virtual void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) = 0;
519 void DoResolveOrReject(ResolveOrRejectValue
& aValue
) {
520 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
521 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget
->IsOnCurrentThread());
522 Request::mComplete
= true;
523 if (Request::mDisconnected
) {
525 "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
530 // Invoke the resolve or reject method.
531 DoResolveOrRejectInternal(aValue
);
534 nsCOMPtr
<nsISerialEventTarget
>
535 mResponseTarget
; // May be released on any thread.
536 # ifdef PROMISE_DEBUG
537 uint32_t mMagic1
= sMagic
;
539 const char* mCallSite
;
540 # ifdef PROMISE_DEBUG
541 uint32_t mMagic2
= sMagic
;
546 * We create two overloads for invoking Resolve/Reject Methods so as to
547 * make the resolve/reject value argument "optional".
549 template <typename ThisType
, typename MethodType
, typename ValueType
>
550 static std::enable_if_t
<TakesArgument
<MethodType
>::value
,
551 typename
detail::MethodTrait
<MethodType
>::ReturnType
>
552 InvokeMethod(ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
) {
553 return (aThisVal
->*aMethod
)(std::forward
<ValueType
>(aValue
));
556 template <typename ThisType
, typename MethodType
, typename ValueType
>
557 static std::enable_if_t
<!TakesArgument
<MethodType
>::value
,
558 typename
detail::MethodTrait
<MethodType
>::ReturnType
>
559 InvokeMethod(ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
) {
560 return (aThisVal
->*aMethod
)();
563 // Called when promise chaining is supported.
564 template <bool SupportChaining
, typename ThisType
, typename MethodType
,
565 typename ValueType
, typename CompletionPromiseType
>
566 static std::enable_if_t
<SupportChaining
, void> InvokeCallbackMethod(
567 ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
,
568 CompletionPromiseType
&& aCompletionPromise
) {
569 auto p
= InvokeMethod(aThisVal
, aMethod
, std::forward
<ValueType
>(aValue
));
570 if (aCompletionPromise
) {
571 p
->ChainTo(aCompletionPromise
.forget(), "<chained completion promise>");
575 // Called when promise chaining is not supported.
576 template <bool SupportChaining
, typename ThisType
, typename MethodType
,
577 typename ValueType
, typename CompletionPromiseType
>
578 static std::enable_if_t
<!SupportChaining
, void> InvokeCallbackMethod(
579 ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
,
580 CompletionPromiseType
&& aCompletionPromise
) {
581 MOZ_DIAGNOSTIC_ASSERT(
583 "Can't do promise chaining for a non-promise-returning method.");
584 InvokeMethod(aThisVal
, aMethod
, std::forward
<ValueType
>(aValue
));
590 template <typename
...>
593 template <typename ThisType
, typename ResolveMethodType
,
594 typename RejectMethodType
>
595 class ThenValue
<ThisType
*, ResolveMethodType
, RejectMethodType
>
596 : public ThenValueBase
{
597 friend class ThenCommand
<ThenValue
>;
599 using R1
= typename RemoveSmartPointer
<
600 typename
detail::MethodTrait
<ResolveMethodType
>::ReturnType
>::Type
;
601 using R2
= typename RemoveSmartPointer
<
602 typename
detail::MethodTrait
<RejectMethodType
>::ReturnType
>::Type
;
603 using SupportChaining
=
604 std::integral_constant
<bool, IsMozPromise
<R1
>::value
&&
605 std::is_same_v
<R1
, R2
>>;
607 // Fall back to MozPromise when promise chaining is not supported to make
610 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
613 ThenValue(nsISerialEventTarget
* aResponseTarget
, ThisType
* aThisVal
,
614 ResolveMethodType aResolveMethod
, RejectMethodType aRejectMethod
,
615 const char* aCallSite
)
616 : ThenValueBase(aResponseTarget
, aCallSite
),
618 mResolveMethod(aResolveMethod
),
619 mRejectMethod(aRejectMethod
) {}
621 void Disconnect() override
{
622 ThenValueBase::Disconnect();
624 // If a Request has been disconnected, we don't guarantee that the
625 // resolve/reject runnable will be dispatched. Null out our refcounted
626 // this-value now so that it's released predictably on the dispatch
632 MozPromiseBase
* CompletionPromise() const override
{
633 return mCompletionPromise
;
636 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
637 if (aValue
.IsResolve()) {
638 InvokeCallbackMethod
<SupportChaining::value
>(
639 mThisVal
.get(), mResolveMethod
, MaybeMove(aValue
.ResolveValue()),
640 std::move(mCompletionPromise
));
642 InvokeCallbackMethod
<SupportChaining::value
>(
643 mThisVal
.get(), mRejectMethod
, MaybeMove(aValue
.RejectValue()),
644 std::move(mCompletionPromise
));
647 // Null out mThisVal after invoking the callback so that any references
648 // are released predictably on the dispatch thread. Otherwise, it would be
649 // released on whatever thread last drops its reference to the ThenValue,
650 // which may or may not be ok.
656 mThisVal
; // Only accessed and refcounted on dispatch thread.
657 ResolveMethodType mResolveMethod
;
658 RejectMethodType mRejectMethod
;
659 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
662 template <typename ThisType
, typename ResolveRejectMethodType
>
663 class ThenValue
<ThisType
*, ResolveRejectMethodType
> : public ThenValueBase
{
664 friend class ThenCommand
<ThenValue
>;
666 using R1
= typename RemoveSmartPointer
<typename
detail::MethodTrait
<
667 ResolveRejectMethodType
>::ReturnType
>::Type
;
668 using SupportChaining
=
669 std::integral_constant
<bool, IsMozPromise
<R1
>::value
>;
671 // Fall back to MozPromise when promise chaining is not supported to make
674 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
677 ThenValue(nsISerialEventTarget
* aResponseTarget
, ThisType
* aThisVal
,
678 ResolveRejectMethodType aResolveRejectMethod
,
679 const char* aCallSite
)
680 : ThenValueBase(aResponseTarget
, aCallSite
),
682 mResolveRejectMethod(aResolveRejectMethod
) {}
684 void Disconnect() override
{
685 ThenValueBase::Disconnect();
687 // If a Request has been disconnected, we don't guarantee that the
688 // resolve/reject runnable will be dispatched. Null out our refcounted
689 // this-value now so that it's released predictably on the dispatch
695 MozPromiseBase
* CompletionPromise() const override
{
696 return mCompletionPromise
;
699 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
700 InvokeCallbackMethod
<SupportChaining::value
>(
701 mThisVal
.get(), mResolveRejectMethod
, MaybeMove(aValue
),
702 std::move(mCompletionPromise
));
704 // Null out mThisVal after invoking the callback so that any references
705 // are released predictably on the dispatch thread. Otherwise, it would be
706 // released on whatever thread last drops its reference to the ThenValue,
707 // which may or may not be ok.
713 mThisVal
; // Only accessed and refcounted on dispatch thread.
714 ResolveRejectMethodType mResolveRejectMethod
;
715 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
718 // NB: We could use std::function here instead of a template if it were
720 template <typename ResolveFunction
, typename RejectFunction
>
721 class ThenValue
<ResolveFunction
, RejectFunction
> : public ThenValueBase
{
722 friend class ThenCommand
<ThenValue
>;
724 using R1
= typename RemoveSmartPointer
<
725 typename
detail::MethodTrait
<ResolveFunction
>::ReturnType
>::Type
;
726 using R2
= typename RemoveSmartPointer
<
727 typename
detail::MethodTrait
<RejectFunction
>::ReturnType
>::Type
;
728 using SupportChaining
=
729 std::integral_constant
<bool, IsMozPromise
<R1
>::value
&&
730 std::is_same_v
<R1
, R2
>>;
732 // Fall back to MozPromise when promise chaining is not supported to make
735 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
738 ThenValue(nsISerialEventTarget
* aResponseTarget
,
739 ResolveFunction
&& aResolveFunction
,
740 RejectFunction
&& aRejectFunction
, const char* aCallSite
)
741 : ThenValueBase(aResponseTarget
, aCallSite
) {
742 mResolveFunction
.emplace(std::move(aResolveFunction
));
743 mRejectFunction
.emplace(std::move(aRejectFunction
));
746 void Disconnect() override
{
747 ThenValueBase::Disconnect();
749 // If a Request has been disconnected, we don't guarantee that the
750 // resolve/reject runnable will be dispatched. Destroy our callbacks
751 // now so that any references in closures are released predictable on
752 // the dispatch thread.
753 mResolveFunction
.reset();
754 mRejectFunction
.reset();
758 MozPromiseBase
* CompletionPromise() const override
{
759 return mCompletionPromise
;
762 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
763 // Note: The usage of InvokeCallbackMethod here requires that
764 // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
765 // classes with ::operator()), since it allows us to share code more
766 // easily. We could fix this if need be, though it's quite easy to work
767 // around by just capturing something.
768 if (aValue
.IsResolve()) {
769 InvokeCallbackMethod
<SupportChaining::value
>(
770 mResolveFunction
.ptr(), &ResolveFunction::operator(),
771 MaybeMove(aValue
.ResolveValue()), std::move(mCompletionPromise
));
773 InvokeCallbackMethod
<SupportChaining::value
>(
774 mRejectFunction
.ptr(), &RejectFunction::operator(),
775 MaybeMove(aValue
.RejectValue()), std::move(mCompletionPromise
));
778 // Destroy callbacks after invocation so that any references in closures
779 // are released predictably on the dispatch thread. Otherwise, they would
780 // be released on whatever thread last drops its reference to the
781 // ThenValue, which may or may not be ok.
782 mResolveFunction
.reset();
783 mRejectFunction
.reset();
787 Maybe
<ResolveFunction
>
788 mResolveFunction
; // Only accessed and deleted on dispatch thread.
789 Maybe
<RejectFunction
>
790 mRejectFunction
; // Only accessed and deleted on dispatch thread.
791 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
794 template <typename ResolveRejectFunction
>
795 class ThenValue
<ResolveRejectFunction
> : public ThenValueBase
{
796 friend class ThenCommand
<ThenValue
>;
798 using R1
= typename RemoveSmartPointer
<
799 typename
detail::MethodTrait
<ResolveRejectFunction
>::ReturnType
>::Type
;
800 using SupportChaining
=
801 std::integral_constant
<bool, IsMozPromise
<R1
>::value
>;
803 // Fall back to MozPromise when promise chaining is not supported to make
806 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
809 ThenValue(nsISerialEventTarget
* aResponseTarget
,
810 ResolveRejectFunction
&& aResolveRejectFunction
,
811 const char* aCallSite
)
812 : ThenValueBase(aResponseTarget
, aCallSite
) {
813 mResolveRejectFunction
.emplace(std::move(aResolveRejectFunction
));
816 void Disconnect() override
{
817 ThenValueBase::Disconnect();
819 // If a Request has been disconnected, we don't guarantee that the
820 // resolve/reject runnable will be dispatched. Destroy our callbacks
821 // now so that any references in closures are released predictable on
822 // the dispatch thread.
823 mResolveRejectFunction
.reset();
827 MozPromiseBase
* CompletionPromise() const override
{
828 return mCompletionPromise
;
831 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
832 // Note: The usage of InvokeCallbackMethod here requires that
833 // ResolveRejectFunction is capture-lambdas (i.e. anonymous
834 // classes with ::operator()), since it allows us to share code more
835 // easily. We could fix this if need be, though it's quite easy to work
836 // around by just capturing something.
837 InvokeCallbackMethod
<SupportChaining::value
>(
838 mResolveRejectFunction
.ptr(), &ResolveRejectFunction::operator(),
839 MaybeMove(aValue
), std::move(mCompletionPromise
));
841 // Destroy callbacks after invocation so that any references in closures
842 // are released predictably on the dispatch thread. Otherwise, they would
843 // be released on whatever thread last drops its reference to the
844 // ThenValue, which may or may not be ok.
845 mResolveRejectFunction
.reset();
849 Maybe
<ResolveRejectFunction
>
850 mResolveRejectFunction
; // Only accessed and deleted on dispatch
852 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
856 void ThenInternal(already_AddRefed
<ThenValueBase
> aThenValue
,
857 const char* aCallSite
) {
858 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
859 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
860 RefPtr
<ThenValueBase
> thenValue
= aThenValue
;
861 MutexAutoLock
lock(mMutex
);
862 MOZ_DIAGNOSTIC_ASSERT(
863 !IsExclusive
|| !mHaveRequest
,
864 "Using an exclusive promise in a non-exclusive fashion");
866 PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
867 aCallSite
, this, thenValue
.get(), (int)IsPending());
869 thenValue
->Dispatch(this);
871 mThenValues
.AppendElement(thenValue
.forget());
877 * A command object to store all information needed to make a request to
878 * the promise. This allows us to delay the request until further use is
879 * known (whether it is ->Then() again for more promise chaining or ->Track()
880 * to terminate chaining and issue the request).
882 * This allows a unified syntax for promise chaining and disconnection
883 * and feels more like its JS counterpart.
885 template <typename ThenValueType
>
887 // Allow Promise1::ThenCommand to access the private constructor,
888 // Promise2::ThenCommand(ThenCommand&&).
889 template <typename
, typename
, bool>
890 friend class MozPromise
;
892 using PromiseType
= typename
ThenValueType::PromiseType
;
893 using Private
= typename
PromiseType::Private
;
895 ThenCommand(const char* aCallSite
,
896 already_AddRefed
<ThenValueType
> aThenValue
,
897 MozPromise
* aReceiver
)
898 : mCallSite(aCallSite
), mThenValue(aThenValue
), mReceiver(aReceiver
) {}
900 ThenCommand(ThenCommand
&& aOther
) = default;
904 // Issue the request now if the return value of Then() is not used.
906 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
910 // Allow RefPtr<MozPromise> p = somePromise->Then();
911 // p->Then(thread1, ...);
912 // p->Then(thread2, ...);
913 operator RefPtr
<PromiseType
>() {
915 ThenValueType::SupportChaining::value
,
916 "The resolve/reject callback needs to return a RefPtr<MozPromise> "
917 "in order to do promise chaining.");
919 // mCompletionPromise must be created before ThenInternal() to avoid race.
921 new Private("<completion promise>", true /* aIsCompletionPromise */);
922 mThenValue
->mCompletionPromise
= p
;
923 // Note ThenInternal() might nullify mCompletionPromise before return.
924 // So we need to return p instead of mCompletionPromise.
925 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
929 template <typename
... Ts
>
930 auto Then(Ts
&&... aArgs
) -> decltype(
931 std::declval
<PromiseType
>().Then(std::forward
<Ts
>(aArgs
)...)) {
932 return static_cast<RefPtr
<PromiseType
>>(*this)->Then(
933 std::forward
<Ts
>(aArgs
)...);
936 void Track(MozPromiseRequestHolder
<MozPromise
>& aRequestHolder
) {
937 aRequestHolder
.Track(do_AddRef(mThenValue
));
938 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
941 // Allow calling ->Then() again for more promise chaining or ->Track() to
942 // end chaining and track the request for future disconnection.
943 ThenCommand
* operator->() { return this; }
946 const char* mCallSite
;
947 RefPtr
<ThenValueType
> mThenValue
;
948 RefPtr
<MozPromise
> mReceiver
;
952 template <typename ThisType
, typename
... Methods
,
953 typename ThenValueType
= ThenValue
<ThisType
*, Methods
...>,
954 typename ReturnType
= ThenCommand
<ThenValueType
>>
955 ReturnType
Then(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
,
956 ThisType
* aThisVal
, Methods
... aMethods
) {
957 RefPtr
<ThenValueType
> thenValue
=
958 new ThenValueType(aResponseTarget
, aThisVal
, aMethods
..., aCallSite
);
959 return ReturnType(aCallSite
, thenValue
.forget(), this);
962 template <typename
... Functions
,
963 typename ThenValueType
= ThenValue
<Functions
...>,
964 typename ReturnType
= ThenCommand
<ThenValueType
>>
965 ReturnType
Then(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
,
966 Functions
&&... aFunctions
) {
967 RefPtr
<ThenValueType
> thenValue
=
968 new ThenValueType(aResponseTarget
, std::move(aFunctions
)..., aCallSite
);
969 return ReturnType(aCallSite
, thenValue
.forget(), this);
972 void ChainTo(already_AddRefed
<Private
> aChainedPromise
,
973 const char* aCallSite
) {
974 MutexAutoLock
lock(mMutex
);
975 MOZ_DIAGNOSTIC_ASSERT(
976 !IsExclusive
|| !mHaveRequest
,
977 "Using an exclusive promise in a non-exclusive fashion");
979 RefPtr
<Private
> chainedPromise
= aChainedPromise
;
981 "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
982 aCallSite
, this, chainedPromise
.get(), (int)IsPending());
984 // We want to use the same type of dispatching method with the chained
987 // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
988 // at compilation time to ensure we're not triggering the static_assert in
989 // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
991 if (mUseDirectTaskDispatch
) {
992 chainedPromise
->UseDirectTaskDispatch(aCallSite
);
993 } else if constexpr (IsExclusive
) {
994 if (mUseSynchronousTaskDispatch
) {
995 chainedPromise
->UseSynchronousTaskDispatch(aCallSite
);
1000 ForwardTo(chainedPromise
);
1002 mChainedPromises
.AppendElement(chainedPromise
);
1006 # ifdef MOZ_WIDGET_ANDROID
1007 // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
1008 [[nodiscard
]] static RefPtr
<MozPromise
> FromGeckoResult(
1009 java::GeckoResult::Param aGeckoResult
) {
1010 using jni::GeckoResultCallback
;
1011 RefPtr
<Private
> p
= new Private("GeckoResult Glue", false);
1012 auto resolve
= GeckoResultCallback::CreateAndAttach
<ResolveValueType
>(
1013 [p
](ResolveValueType aArg
) { p
->Resolve(aArg
, __func__
); });
1014 auto reject
= GeckoResultCallback::CreateAndAttach
<RejectValueType
>(
1015 [p
](RejectValueType aArg
) { p
->Reject(aArg
, __func__
); });
1016 aGeckoResult
->NativeThen(resolve
, reject
);
1021 // Creates a C++ MozPromise from its JS counterpart, dom::Promise.
1022 // FromDomPromise currently only supports primitive types (int8/16/32, float,
1023 // double) And the reject value type must be a nsresult.
1024 // To use, please include MozPromiseInlines.h
1025 static RefPtr
<MozPromise
> FromDomPromise(dom::Promise
* aDOMPromise
);
1027 // Note we expose the function AssertIsDead() instead of IsDead() since
1028 // checking IsDead() is a data race in the situation where the request is not
1029 // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1030 // AssertIsDead() only.
1031 void AssertIsDead() override
{
1032 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1033 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1034 MutexAutoLock
lock(mMutex
);
1035 for (auto&& then
: mThenValues
) {
1036 then
->AssertIsDead();
1038 for (auto&& chained
: mChainedPromises
) {
1039 chained
->AssertIsDead();
1044 bool IsPending() const { return mValue
.IsNothing(); }
1046 ResolveOrRejectValue
& Value() {
1047 // This method should only be called once the value has stabilized. As
1048 // such, we don't need to acquire the lock here.
1049 MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1053 void DispatchAll() {
1054 mMutex
.AssertCurrentThreadOwns();
1055 for (auto&& thenValue
: mThenValues
) {
1056 thenValue
->Dispatch(this);
1058 mThenValues
.Clear();
1060 for (auto&& chainedPromise
: mChainedPromises
) {
1061 ForwardTo(chainedPromise
);
1063 mChainedPromises
.Clear();
1066 void ForwardTo(Private
* aOther
) {
1067 MOZ_ASSERT(!IsPending());
1068 if (mValue
.IsResolve()) {
1069 aOther
->Resolve(MaybeMove(mValue
.ResolveValue()), "<chained promise>");
1071 aOther
->Reject(MaybeMove(mValue
.RejectValue()), "<chained promise>");
1075 virtual ~MozPromise() {
1076 PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1078 // We can't guarantee a completion promise will always be revolved or
1079 // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1080 if (!mIsCompletionPromise
) {
1081 MOZ_ASSERT(!IsPending());
1082 MOZ_ASSERT(mThenValues
.IsEmpty());
1083 MOZ_ASSERT(mChainedPromises
.IsEmpty());
1085 # ifdef PROMISE_DEBUG
1093 const char* mCreationSite
; // For logging
1095 ResolveOrRejectValue mValue
;
1096 bool mUseSynchronousTaskDispatch
= false;
1097 bool mUseDirectTaskDispatch
= false;
1098 # ifdef PROMISE_DEBUG
1099 uint32_t mMagic1
= sMagic
;
1101 // Try shows we never have more than 3 elements when IsExclusive is false.
1102 // So '3' is a good value to avoid heap allocation in most cases.
1103 AutoTArray
<RefPtr
<ThenValueBase
>, IsExclusive
? 1 : 3> mThenValues
;
1104 # ifdef PROMISE_DEBUG
1105 uint32_t mMagic2
= sMagic
;
1107 nsTArray
<RefPtr
<Private
>> mChainedPromises
;
1108 # ifdef PROMISE_DEBUG
1109 uint32_t mMagic3
= sMagic
;
1112 const bool mIsCompletionPromise
;
1113 # ifdef PROMISE_DEBUG
1118 template <typename ResolveValueT
, typename RejectValueT
, bool IsExclusive
>
1119 class MozPromise
<ResolveValueT
, RejectValueT
, IsExclusive
>::Private
1120 : public MozPromise
<ResolveValueT
, RejectValueT
, IsExclusive
> {
1122 explicit Private(const char* aCreationSite
, bool aIsCompletionPromise
= false)
1123 : MozPromise(aCreationSite
, aIsCompletionPromise
) {}
1125 template <typename ResolveValueT_
>
1126 void Resolve(ResolveValueT_
&& aResolveValue
, const char* aResolveSite
) {
1127 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1128 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1129 MutexAutoLock
lock(mMutex
);
1130 PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite
,
1131 this, mCreationSite
);
1134 "%s ignored already resolved or rejected MozPromise (%p created at "
1136 aResolveSite
, this, mCreationSite
);
1139 mValue
.SetResolve(std::forward
<ResolveValueT_
>(aResolveValue
));
1143 template <typename RejectValueT_
>
1144 void Reject(RejectValueT_
&& aRejectValue
, const char* aRejectSite
) {
1145 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1146 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1147 MutexAutoLock
lock(mMutex
);
1148 PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite
, this,
1152 "%s ignored already resolved or rejected MozPromise (%p created at "
1154 aRejectSite
, this, mCreationSite
);
1157 mValue
.SetReject(std::forward
<RejectValueT_
>(aRejectValue
));
1161 template <typename ResolveOrRejectValue_
>
1162 void ResolveOrReject(ResolveOrRejectValue_
&& aValue
, const char* aSite
) {
1163 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1164 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1165 MutexAutoLock
lock(mMutex
);
1166 PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite
,
1167 this, mCreationSite
);
1170 "%s ignored already resolved or rejected MozPromise (%p created at "
1172 aSite
, this, mCreationSite
);
1175 mValue
= std::forward
<ResolveOrRejectValue_
>(aValue
);
1179 // If the caller and target are both on the same thread, run the the resolve
1180 // or reject callback synchronously. Otherwise, the task will be dispatched
1181 // via the target Dispatch method.
1182 void UseSynchronousTaskDispatch(const char* aSite
) {
1185 "Synchronous dispatch can only be used with exclusive promises");
1186 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1187 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1188 MutexAutoLock
lock(mMutex
);
1189 PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
1190 aSite
, this, mCreationSite
);
1191 MOZ_ASSERT(IsPending(),
1192 "A Promise must not have been already resolved or rejected to "
1193 "set dispatch state");
1194 mUseSynchronousTaskDispatch
= true;
1197 // If the caller and target are both on the same thread, run the
1198 // resolve/reject callback off the direct task queue instead. This avoids a
1199 // full trip to the back of the event queue for each additional asynchronous
1200 // step when using MozPromise, and is similar (but not identical to) the
1201 // microtask semantics of JS promises.
1202 void UseDirectTaskDispatch(const char* aSite
) {
1203 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1204 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1205 MutexAutoLock
lock(mMutex
);
1206 PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite
,
1207 this, mCreationSite
);
1208 MOZ_ASSERT(IsPending(),
1209 "A Promise must not have been already resolved or rejected to "
1210 "set dispatch state");
1211 MOZ_ASSERT(!mUseSynchronousTaskDispatch
,
1212 "Promise already set for synchronous dispatch");
1213 mUseDirectTaskDispatch
= true;
1217 // A generic promise type that does the trick for simple use cases.
1218 typedef MozPromise
<bool, nsresult
, /* IsExclusive = */ true> GenericPromise
;
1220 // A generic, non-exclusive promise type that does the trick for simple use
1222 typedef MozPromise
<bool, nsresult
, /* IsExclusive = */ false>
1223 GenericNonExclusivePromise
;
1226 * Class to encapsulate a promise for a particular role. Use this as the member
1227 * variable for a class whose method returns a promise.
1229 template <typename PromiseType
, typename ImplType
>
1230 class MozPromiseHolderBase
{
1232 MozPromiseHolderBase() = default;
1234 MozPromiseHolderBase(MozPromiseHolderBase
&& aOther
) = default;
1235 MozPromiseHolderBase
& operator=(MozPromiseHolderBase
&& aOther
) = default;
1237 ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise
); }
1239 already_AddRefed
<PromiseType
> Ensure(const char* aMethodName
) {
1240 static_cast<ImplType
*>(this)->Check();
1242 mPromise
= new (typename
PromiseType::Private
)(aMethodName
);
1244 RefPtr
<PromiseType
> p
= mPromise
.get();
1248 bool IsEmpty() const {
1249 static_cast<const ImplType
*>(this)->Check();
1253 already_AddRefed
<typename
PromiseType::Private
> Steal() {
1254 static_cast<ImplType
*>(this)->Check();
1255 return mPromise
.forget();
1258 template <typename ResolveValueType_
>
1259 void Resolve(ResolveValueType_
&& aResolveValue
, const char* aMethodName
) {
1260 static_assert(std::is_convertible_v
<ResolveValueType_
,
1261 typename
PromiseType::ResolveValueType
>,
1262 "Resolve() argument must be implicitly convertible to "
1263 "MozPromise's ResolveValueT");
1265 static_cast<ImplType
*>(this)->Check();
1266 MOZ_ASSERT(mPromise
);
1267 mPromise
->Resolve(std::forward
<ResolveValueType_
>(aResolveValue
),
1272 template <typename ResolveValueType_
>
1273 void ResolveIfExists(ResolveValueType_
&& aResolveValue
,
1274 const char* aMethodName
) {
1276 Resolve(std::forward
<ResolveValueType_
>(aResolveValue
), aMethodName
);
1280 template <typename RejectValueType_
>
1281 void Reject(RejectValueType_
&& aRejectValue
, const char* aMethodName
) {
1282 static_assert(std::is_convertible_v
<RejectValueType_
,
1283 typename
PromiseType::RejectValueType
>,
1284 "Reject() argument must be implicitly convertible to "
1285 "MozPromise's RejectValueT");
1287 static_cast<ImplType
*>(this)->Check();
1288 MOZ_ASSERT(mPromise
);
1289 mPromise
->Reject(std::forward
<RejectValueType_
>(aRejectValue
), aMethodName
);
1293 template <typename RejectValueType_
>
1294 void RejectIfExists(RejectValueType_
&& aRejectValue
,
1295 const char* aMethodName
) {
1297 Reject(std::forward
<RejectValueType_
>(aRejectValue
), aMethodName
);
1301 template <typename ResolveOrRejectValueType_
>
1302 void ResolveOrReject(ResolveOrRejectValueType_
&& aValue
,
1303 const char* aMethodName
) {
1304 static_cast<ImplType
*>(this)->Check();
1305 MOZ_ASSERT(mPromise
);
1306 mPromise
->ResolveOrReject(std::forward
<ResolveOrRejectValueType_
>(aValue
),
1311 template <typename ResolveOrRejectValueType_
>
1312 void ResolveOrRejectIfExists(ResolveOrRejectValueType_
&& aValue
,
1313 const char* aMethodName
) {
1315 ResolveOrReject(std::forward
<ResolveOrRejectValueType_
>(aValue
),
1320 void UseSynchronousTaskDispatch(const char* aSite
) {
1321 MOZ_ASSERT(mPromise
);
1322 mPromise
->UseSynchronousTaskDispatch(aSite
);
1325 void UseDirectTaskDispatch(const char* aSite
) {
1326 MOZ_ASSERT(mPromise
);
1327 mPromise
->UseDirectTaskDispatch(aSite
);
1331 RefPtr
<typename
PromiseType::Private
> mPromise
;
1334 template <typename PromiseType
>
1335 class MozPromiseHolder
1336 : public MozPromiseHolderBase
<PromiseType
, MozPromiseHolder
<PromiseType
>> {
1338 using MozPromiseHolderBase
<
1339 PromiseType
, MozPromiseHolder
<PromiseType
>>::MozPromiseHolderBase
;
1340 static constexpr void Check(){};
1343 template <typename PromiseType
>
1344 class MozMonitoredPromiseHolder
1345 : public MozPromiseHolderBase
<PromiseType
,
1346 MozMonitoredPromiseHolder
<PromiseType
>> {
1348 // Provide a Monitor that should always be held when accessing this instance.
1349 explicit MozMonitoredPromiseHolder(Monitor
* const aMonitor
)
1350 : mMonitor(aMonitor
) {
1351 MOZ_ASSERT(aMonitor
);
1354 MozMonitoredPromiseHolder(MozMonitoredPromiseHolder
&& aOther
) = delete;
1355 MozMonitoredPromiseHolder
& operator=(MozMonitoredPromiseHolder
&& aOther
) =
1358 void Check() const { mMonitor
->AssertCurrentThreadOwns(); }
1361 Monitor
* const mMonitor
;
1365 * Class to encapsulate a MozPromise::Request reference. Use this as the member
1366 * variable for a class waiting on a MozPromise.
1368 template <typename PromiseType
>
1369 class MozPromiseRequestHolder
{
1371 MozPromiseRequestHolder() = default;
1372 ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest
); }
1374 void Track(already_AddRefed
<typename
PromiseType::Request
> aRequest
) {
1375 MOZ_DIAGNOSTIC_ASSERT(!Exists());
1376 mRequest
= aRequest
;
1380 MOZ_DIAGNOSTIC_ASSERT(Exists());
1384 // Disconnects and forgets an outstanding promise. The resolve/reject methods
1385 // will never be called.
1387 MOZ_ASSERT(Exists());
1388 mRequest
->Disconnect();
1392 void DisconnectIfExists() {
1398 bool Exists() const { return !!mRequest
; }
1401 RefPtr
<typename
PromiseType::Request
> mRequest
;
1404 // Asynchronous Potentially-Cross-Thread Method Calls.
1406 // This machinery allows callers to schedule a promise-returning function
1407 // (a method and object, or a function object like a lambda) to be invoked
1408 // asynchronously on a given thread, while at the same time receiving a
1409 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1410 // task to invoke the function on the proper thread and also chain the
1411 // resulting promise to the one that the caller received, so that resolve/
1412 // reject values are forwarded through.
1416 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1417 // assertions when used on templated types.
1418 class MethodCallBase
{
1420 MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase
)
1421 MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase
)
1424 template <typename PromiseType
, typename MethodType
, typename ThisType
,
1425 typename
... Storages
>
1426 class MethodCall
: public MethodCallBase
{
1428 template <typename
... Args
>
1429 MethodCall(MethodType aMethod
, ThisType
* aThisVal
, Args
&&... aArgs
)
1432 mArgs(std::forward
<Args
>(aArgs
)...) {
1433 static_assert(sizeof...(Storages
) == sizeof...(Args
),
1434 "Storages and Args should have equal sizes");
1437 RefPtr
<PromiseType
> Invoke() { return mArgs
.apply(mThisVal
.get(), mMethod
); }
1441 RefPtr
<ThisType
> mThisVal
;
1442 RunnableMethodArguments
<Storages
...> mArgs
;
1445 template <typename PromiseType
, typename MethodType
, typename ThisType
,
1446 typename
... Storages
>
1447 class ProxyRunnable
: public CancelableRunnable
{
1450 typename
PromiseType::Private
* aProxyPromise
,
1451 MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>* aMethodCall
)
1452 : CancelableRunnable("detail::ProxyRunnable"),
1453 mProxyPromise(aProxyPromise
),
1454 mMethodCall(aMethodCall
) {}
1456 NS_IMETHOD
Run() override
{
1457 RefPtr
<PromiseType
> p
= mMethodCall
->Invoke();
1458 mMethodCall
= nullptr;
1459 p
->ChainTo(mProxyPromise
.forget(), "<Proxy Promise>");
1463 nsresult
Cancel() override
{ return Run(); }
1466 RefPtr
<typename
PromiseType::Private
> mProxyPromise
;
1467 UniquePtr
<MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>>
1471 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1472 typename
... ArgTypes
, typename
... ActualArgTypes
>
1473 static RefPtr
<PromiseType
> InvokeAsyncImpl(
1474 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1475 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1476 ActualArgTypes
&&... aArgs
) {
1477 MOZ_ASSERT(aTarget
);
1479 typedef RefPtr
<PromiseType
> (ThisType::*MethodType
)(ArgTypes
...);
1480 typedef detail::MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>
1482 typedef detail::ProxyRunnable
<PromiseType
, MethodType
, ThisType
, Storages
...>
1485 MethodCallType
* methodCall
= new MethodCallType(
1486 aMethod
, aThisVal
, std::forward
<ActualArgTypes
>(aArgs
)...);
1487 RefPtr
<typename
PromiseType::Private
> p
=
1488 new (typename
PromiseType::Private
)(aCallerName
);
1489 RefPtr
<ProxyRunnableType
> r
= new ProxyRunnableType(p
, methodCall
);
1490 aTarget
->Dispatch(r
.forget());
1494 constexpr bool Any() { return false; }
1496 template <typename T1
>
1497 constexpr bool Any(T1 a
) {
1498 return static_cast<bool>(a
);
1501 template <typename T1
, typename
... Ts
>
1502 constexpr bool Any(T1 a
, Ts
... aOthers
) {
1503 return a
|| Any(aOthers
...);
1506 } // namespace detail
1508 // InvokeAsync with explicitly-specified storages.
1509 // See ParameterStorage in nsThreadUtils.h for help.
1510 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1511 typename
... ArgTypes
, typename
... ActualArgTypes
,
1512 std::enable_if_t
<sizeof...(Storages
) != 0, int> = 0>
1513 static RefPtr
<PromiseType
> InvokeAsync(
1514 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1515 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1516 ActualArgTypes
&&... aArgs
) {
1518 sizeof...(Storages
) == sizeof...(ArgTypes
),
1519 "Provided Storages and method's ArgTypes should have equal sizes");
1520 static_assert(sizeof...(Storages
) == sizeof...(ActualArgTypes
),
1521 "Provided Storages and ActualArgTypes should have equal sizes");
1522 return detail::InvokeAsyncImpl
<Storages
...>(
1523 aTarget
, aThisVal
, aCallerName
, aMethod
,
1524 std::forward
<ActualArgTypes
>(aArgs
)...);
1527 // InvokeAsync with no explicitly-specified storages, will copy arguments and
1528 // then move them out of the runnable into the target method parameters.
1529 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1530 typename
... ArgTypes
, typename
... ActualArgTypes
,
1531 std::enable_if_t
<sizeof...(Storages
) == 0, int> = 0>
1532 static RefPtr
<PromiseType
> InvokeAsync(
1533 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1534 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1535 ActualArgTypes
&&... aArgs
) {
1538 std::is_pointer_v
<std::remove_reference_t
<ActualArgTypes
>>...),
1539 "Cannot pass pointer types through InvokeAsync, Storages must be "
1541 static_assert(sizeof...(ArgTypes
) == sizeof...(ActualArgTypes
),
1542 "Method's ArgTypes and ActualArgTypes should have equal sizes");
1543 return detail::InvokeAsyncImpl
<
1544 StoreCopyPassByRRef
<std::decay_t
<ActualArgTypes
>>...>(
1545 aTarget
, aThisVal
, aCallerName
, aMethod
,
1546 std::forward
<ActualArgTypes
>(aArgs
)...);
1551 template <typename Function
, typename PromiseType
>
1552 class ProxyFunctionRunnable
: public CancelableRunnable
{
1553 using FunctionStorage
= std::decay_t
<Function
>;
1556 template <typename F
>
1557 ProxyFunctionRunnable(typename
PromiseType::Private
* aProxyPromise
,
1559 : CancelableRunnable("detail::ProxyFunctionRunnable"),
1560 mProxyPromise(aProxyPromise
),
1561 mFunction(new FunctionStorage(std::forward
<F
>(aFunction
))) {}
1563 NS_IMETHOD
Run() override
{
1564 RefPtr
<PromiseType
> p
= (*mFunction
)();
1565 mFunction
= nullptr;
1566 p
->ChainTo(mProxyPromise
.forget(), "<Proxy Promise>");
1570 nsresult
Cancel() override
{ return Run(); }
1573 RefPtr
<typename
PromiseType::Private
> mProxyPromise
;
1574 UniquePtr
<FunctionStorage
> mFunction
;
1577 // Note: The following struct and function are not for public consumption (yet?)
1578 // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1579 // function objects). They could be moved outside of detail if really needed.
1581 // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1582 // and their captures). This struct is a tag that allows the use of objects
1583 // through lvalue-refs where necessary.
1584 struct AllowInvokeAsyncFunctionLVRef
{};
1586 // Invoke a function object (e.g., lambda or std/mozilla::function)
1587 // asynchronously; note that the object will be copied if provided by
1588 // lvalue-ref. Return a promise that the function should eventually resolve or
1590 template <typename Function
>
1591 static auto InvokeAsync(nsISerialEventTarget
* aTarget
, const char* aCallerName
,
1592 AllowInvokeAsyncFunctionLVRef
, Function
&& aFunction
)
1593 -> decltype(aFunction()) {
1595 IsRefcountedSmartPointer
<decltype(aFunction())>::value
&&
1597 typename RemoveSmartPointer
<decltype(aFunction())>::Type
>::value
,
1598 "Function object must return RefPtr<MozPromise>");
1599 MOZ_ASSERT(aTarget
);
1600 typedef typename RemoveSmartPointer
<decltype(aFunction())>::Type PromiseType
;
1601 typedef detail::ProxyFunctionRunnable
<Function
, PromiseType
>
1604 auto p
= MakeRefPtr
<typename
PromiseType::Private
>(aCallerName
);
1605 auto r
= MakeRefPtr
<ProxyRunnableType
>(p
, std::forward
<Function
>(aFunction
));
1606 aTarget
->Dispatch(r
.forget());
1610 } // namespace detail
1612 // Invoke a function object (e.g., lambda) asynchronously.
1613 // Return a promise that the function should eventually resolve or reject.
1614 template <typename Function
>
1615 static auto InvokeAsync(nsISerialEventTarget
* aTarget
, const char* aCallerName
,
1616 Function
&& aFunction
) -> decltype(aFunction()) {
1617 static_assert(!std::is_lvalue_reference_v
<Function
>,
1618 "Function object must not be passed by lvalue-ref (to avoid "
1619 "unplanned copies); Consider move()ing the object.");
1620 return detail::InvokeAsync(aTarget
, aCallerName
,
1621 detail::AllowInvokeAsyncFunctionLVRef(),
1622 std::forward
<Function
>(aFunction
));
1626 # undef PROMISE_ASSERT
1627 # undef PROMISE_DEBUG
1629 } // namespace mozilla