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/ErrorNames.h"
14 # include "mozilla/Logging.h"
15 # include "mozilla/Maybe.h"
16 # include "mozilla/Monitor.h"
17 # include "mozilla/Mutex.h"
18 # include "mozilla/RefPtr.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
,
295 typedef MozPromise
<CopyableTArray
<ResolveOrRejectValue
>, bool, IsExclusive
>
296 AllSettledPromiseType
;
299 class AllPromiseHolder
: public MozPromiseRefcountable
{
301 explicit AllPromiseHolder(size_t aDependentPromises
)
302 : mPromise(new typename
AllPromiseType::Private(__func__
)),
303 mOutstandingPromises(aDependentPromises
) {
304 MOZ_ASSERT(aDependentPromises
> 0);
305 mResolveValues
.SetLength(aDependentPromises
);
308 template <typename ResolveValueType_
>
309 void Resolve(size_t aIndex
, ResolveValueType_
&& aResolveValue
) {
315 mResolveValues
[aIndex
].emplace(
316 std::forward
<ResolveValueType_
>(aResolveValue
));
317 if (--mOutstandingPromises
== 0) {
318 nsTArray
<ResolveValueType
> resolveValues
;
319 resolveValues
.SetCapacity(mResolveValues
.Length());
320 for (auto&& resolveValue
: mResolveValues
) {
321 resolveValues
.AppendElement(std::move(resolveValue
.ref()));
324 mPromise
->Resolve(std::move(resolveValues
), __func__
);
326 mResolveValues
.Clear();
330 template <typename RejectValueType_
>
331 void Reject(RejectValueType_
&& aRejectValue
) {
337 mPromise
->Reject(std::forward
<RejectValueType_
>(aRejectValue
), __func__
);
339 mResolveValues
.Clear();
342 AllPromiseType
* Promise() { return mPromise
; }
345 nsTArray
<Maybe
<ResolveValueType
>> mResolveValues
;
346 RefPtr
<typename
AllPromiseType::Private
> mPromise
;
347 size_t mOutstandingPromises
;
350 // Trying to pass ResolveOrRejectValue by value fails static analysis checks,
351 // so we need to use either a const& or an rvalue reference, depending on
352 // whether IsExclusive is true or not.
353 typedef std::conditional_t
<IsExclusive
, ResolveOrRejectValue
&&,
354 const ResolveOrRejectValue
&>
355 ResolveOrRejectValueParam
;
357 typedef std::conditional_t
<IsExclusive
, ResolveValueType
&&,
358 const ResolveValueType
&>
359 ResolveValueTypeParam
;
361 typedef std::conditional_t
<IsExclusive
, RejectValueType
&&,
362 const RejectValueType
&>
363 RejectValueTypeParam
;
365 class AllSettledPromiseHolder
: public MozPromiseRefcountable
{
367 explicit AllSettledPromiseHolder(size_t aDependentPromises
)
368 : mPromise(new typename
AllSettledPromiseType::Private(__func__
)),
369 mOutstandingPromises(aDependentPromises
) {
370 MOZ_ASSERT(aDependentPromises
> 0);
371 mValues
.SetLength(aDependentPromises
);
374 void Settle(size_t aIndex
, ResolveOrRejectValueParam aValue
) {
380 mValues
[aIndex
].emplace(MaybeMove(aValue
));
381 if (--mOutstandingPromises
== 0) {
382 nsTArray
<ResolveOrRejectValue
> values
;
383 values
.SetCapacity(mValues
.Length());
384 for (auto&& value
: mValues
) {
385 values
.AppendElement(std::move(value
.ref()));
388 mPromise
->Resolve(std::move(values
), __func__
);
394 AllSettledPromiseType
* Promise() { return mPromise
; }
397 nsTArray
<Maybe
<ResolveOrRejectValue
>> mValues
;
398 RefPtr
<typename
AllSettledPromiseType::Private
> mPromise
;
399 size_t mOutstandingPromises
;
403 [[nodiscard
]] static RefPtr
<AllPromiseType
> All(
404 nsISerialEventTarget
* aProcessingTarget
,
405 nsTArray
<RefPtr
<MozPromise
>>& aPromises
) {
406 if (aPromises
.Length() == 0) {
407 return AllPromiseType::CreateAndResolve(
408 CopyableTArray
<ResolveValueType
>(), __func__
);
411 RefPtr
<AllPromiseHolder
> holder
= new AllPromiseHolder(aPromises
.Length());
412 RefPtr
<AllPromiseType
> promise
= holder
->Promise();
413 for (size_t i
= 0; i
< aPromises
.Length(); ++i
) {
415 aProcessingTarget
, __func__
,
416 [holder
, i
](ResolveValueTypeParam aResolveValue
) -> void {
417 holder
->Resolve(i
, MaybeMove(aResolveValue
));
419 [holder
](RejectValueTypeParam aRejectValue
) -> void {
420 holder
->Reject(MaybeMove(aRejectValue
));
426 [[nodiscard
]] static RefPtr
<AllSettledPromiseType
> AllSettled(
427 nsISerialEventTarget
* aProcessingTarget
,
428 nsTArray
<RefPtr
<MozPromise
>>& aPromises
) {
429 if (aPromises
.Length() == 0) {
430 return AllSettledPromiseType::CreateAndResolve(
431 CopyableTArray
<ResolveOrRejectValue
>(), __func__
);
434 RefPtr
<AllSettledPromiseHolder
> holder
=
435 new AllSettledPromiseHolder(aPromises
.Length());
436 RefPtr
<AllSettledPromiseType
> promise
= holder
->Promise();
437 for (size_t i
= 0; i
< aPromises
.Length(); ++i
) {
438 aPromises
[i
]->Then(aProcessingTarget
, __func__
,
439 [holder
, i
](ResolveOrRejectValueParam aValue
) -> void {
440 holder
->Settle(i
, MaybeMove(aValue
));
446 class Request
: public MozPromiseRefcountable
{
448 virtual void Disconnect() = 0;
451 Request() : mComplete(false), mDisconnected(false) {}
452 virtual ~Request() = default;
460 * A ThenValue tracks a single consumer waiting on the promise. When a
461 * consumer invokes promise->Then(...), a ThenValue is created. Once the
462 * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
463 * which invokes the resolve/reject method and then deletes the ThenValue.
465 class ThenValueBase
: public Request
{
466 friend class MozPromise
;
467 static const uint32_t sMagic
= 0xfadece11;
470 class ResolveOrRejectRunnable final
471 : public PrioritizableCancelableRunnable
{
473 ResolveOrRejectRunnable(ThenValueBase
* aThenValue
, MozPromise
* aPromise
)
474 : PrioritizableCancelableRunnable(
476 "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
477 mThenValue(aThenValue
),
479 MOZ_DIAGNOSTIC_ASSERT(!mPromise
->IsPending());
482 ~ResolveOrRejectRunnable() {
484 mThenValue
->AssertIsDead();
488 NS_IMETHOD
Run() override
{
489 PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
490 mThenValue
->DoResolveOrReject(mPromise
->Value());
491 mThenValue
= nullptr;
496 nsresult
Cancel() override
{ return Run(); }
499 RefPtr
<ThenValueBase
> mThenValue
;
500 RefPtr
<MozPromise
> mPromise
;
503 ThenValueBase(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
)
504 : mResponseTarget(aResponseTarget
), mCallSite(aCallSite
) {
505 MOZ_ASSERT(aResponseTarget
);
508 # ifdef PROMISE_DEBUG
515 void AssertIsDead() {
516 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
517 // We want to assert that this ThenValues is dead - that is to say, that
518 // there are no consumers waiting for the result. In the case of a normal
519 // ThenValue, we check that it has been disconnected, which is the way
520 // that the consumer signals that it no longer wishes to hear about the
521 // result. If this ThenValue has a completion promise (which is mutually
522 // exclusive with being disconnectable), we recursively assert that every
523 // ThenValue associated with the completion promise is dead.
524 if (MozPromiseBase
* p
= CompletionPromise()) {
527 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
528 if (MOZ_UNLIKELY(!Request::mDisconnected
)) {
529 MOZ_CRASH_UNSAFE_PRINTF(
530 "MozPromise::ThenValue created from '%s' destroyed without being "
531 "either disconnected, resolved, or rejected (dispatchRv: %s)",
533 mDispatchRv
? GetStaticErrorName(*mDispatchRv
)
540 void Dispatch(MozPromise
* aPromise
) {
541 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
542 aPromise
->mMutex
.AssertCurrentThreadOwns();
543 MOZ_ASSERT(!aPromise
->IsPending());
545 nsCOMPtr
<nsIRunnable
> r
= new ResolveOrRejectRunnable(this, aPromise
);
547 "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
549 aPromise
->mValue
.IsResolve() ? "Resolving" : "Rejecting", mCallSite
,
550 r
.get(), aPromise
, this,
551 aPromise
->mUseSynchronousTaskDispatch
? "synchronous"
552 : aPromise
->mUseDirectTaskDispatch
? "directtask"
555 if (aPromise
->mUseSynchronousTaskDispatch
&&
556 mResponseTarget
->IsOnCurrentThread()) {
557 PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
563 if (aPromise
->mUseDirectTaskDispatch
&&
564 mResponseTarget
->IsOnCurrentThread()) {
566 "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
568 nsCOMPtr
<nsIDirectTaskDispatcher
> dispatcher
=
569 do_QueryInterface(mResponseTarget
);
571 SetDispatchRv(dispatcher
->DispatchDirectTask(r
.forget()));
576 "Direct Task dispatching not available for thread \"%s\"",
577 PR_GetThreadName(PR_GetCurrentThread()))
579 MOZ_DIAGNOSTIC_ASSERT(
581 "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
585 // Promise consumers are allowed to disconnect the Request object and
586 // then shut down the thread or task queue that the promise result would
587 // be dispatched on. So we unfortunately can't assert that promise
588 // dispatch succeeds. :-(
589 // We do record whether or not it succeeds so that if the ThenValueBase is
590 // then destroyed and it was not disconnected, we can include that
591 // information in the assertion message.
592 SetDispatchRv(mResponseTarget
->Dispatch(r
.forget()));
595 void Disconnect() override
{
596 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget
->IsOnCurrentThread());
597 MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete
);
598 Request::mDisconnected
= true;
600 // We could support rejecting the completion promise on disconnection, but
601 // then we'd need to have some sort of default reject value. The use cases
602 // of disconnection and completion promise chaining seem pretty
603 // orthogonal, so let's use assert against it.
604 MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
608 virtual MozPromiseBase
* CompletionPromise() const = 0;
609 virtual void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) = 0;
611 void DoResolveOrReject(ResolveOrRejectValue
& aValue
) {
612 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
);
613 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget
->IsOnCurrentThread());
614 Request::mComplete
= true;
615 if (Request::mDisconnected
) {
617 "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
622 // Invoke the resolve or reject method.
623 DoResolveOrRejectInternal(aValue
);
626 void SetDispatchRv(nsresult aRv
) {
627 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
628 mDispatchRv
= Some(aRv
);
632 nsCOMPtr
<nsISerialEventTarget
>
633 mResponseTarget
; // May be released on any thread.
634 # ifdef PROMISE_DEBUG
635 uint32_t mMagic1
= sMagic
;
637 const char* mCallSite
;
638 # ifdef PROMISE_DEBUG
639 uint32_t mMagic2
= sMagic
;
641 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
642 Maybe
<nsresult
> mDispatchRv
;
647 * We create two overloads for invoking Resolve/Reject Methods so as to
648 * make the resolve/reject value argument "optional".
650 template <typename ThisType
, typename MethodType
, typename ValueType
>
651 static std::enable_if_t
<TakesArgument
<MethodType
>::value
,
652 typename
detail::MethodTrait
<MethodType
>::ReturnType
>
653 InvokeMethod(ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
) {
654 return (aThisVal
->*aMethod
)(std::forward
<ValueType
>(aValue
));
657 template <typename ThisType
, typename MethodType
, typename ValueType
>
658 static std::enable_if_t
<!TakesArgument
<MethodType
>::value
,
659 typename
detail::MethodTrait
<MethodType
>::ReturnType
>
660 InvokeMethod(ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
) {
661 return (aThisVal
->*aMethod
)();
664 // Called when promise chaining is supported.
665 template <bool SupportChaining
, typename ThisType
, typename MethodType
,
666 typename ValueType
, typename CompletionPromiseType
>
667 static std::enable_if_t
<SupportChaining
, void> InvokeCallbackMethod(
668 ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
,
669 CompletionPromiseType
&& aCompletionPromise
) {
670 auto p
= InvokeMethod(aThisVal
, aMethod
, std::forward
<ValueType
>(aValue
));
671 if (aCompletionPromise
) {
672 p
->ChainTo(aCompletionPromise
.forget(), "<chained completion promise>");
676 // Called when promise chaining is not supported.
677 template <bool SupportChaining
, typename ThisType
, typename MethodType
,
678 typename ValueType
, typename CompletionPromiseType
>
679 static std::enable_if_t
<!SupportChaining
, void> InvokeCallbackMethod(
680 ThisType
* aThisVal
, MethodType aMethod
, ValueType
&& aValue
,
681 CompletionPromiseType
&& aCompletionPromise
) {
682 MOZ_DIAGNOSTIC_ASSERT(
684 "Can't do promise chaining for a non-promise-returning method.");
685 InvokeMethod(aThisVal
, aMethod
, std::forward
<ValueType
>(aValue
));
691 template <typename
...>
694 template <typename ThisType
, typename ResolveMethodType
,
695 typename RejectMethodType
>
696 class ThenValue
<ThisType
*, ResolveMethodType
, RejectMethodType
>
697 : public ThenValueBase
{
698 friend class ThenCommand
<ThenValue
>;
700 using R1
= typename RemoveSmartPointer
<
701 typename
detail::MethodTrait
<ResolveMethodType
>::ReturnType
>::Type
;
702 using R2
= typename RemoveSmartPointer
<
703 typename
detail::MethodTrait
<RejectMethodType
>::ReturnType
>::Type
;
704 using SupportChaining
=
705 std::integral_constant
<bool, IsMozPromise
<R1
>::value
&&
706 std::is_same_v
<R1
, R2
>>;
708 // Fall back to MozPromise when promise chaining is not supported to make
711 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
714 ThenValue(nsISerialEventTarget
* aResponseTarget
, ThisType
* aThisVal
,
715 ResolveMethodType aResolveMethod
, RejectMethodType aRejectMethod
,
716 const char* aCallSite
)
717 : ThenValueBase(aResponseTarget
, aCallSite
),
719 mResolveMethod(aResolveMethod
),
720 mRejectMethod(aRejectMethod
) {}
722 void Disconnect() override
{
723 ThenValueBase::Disconnect();
725 // If a Request has been disconnected, we don't guarantee that the
726 // resolve/reject runnable will be dispatched. Null out our refcounted
727 // this-value now so that it's released predictably on the dispatch
733 MozPromiseBase
* CompletionPromise() const override
{
734 return mCompletionPromise
;
737 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
738 if (aValue
.IsResolve()) {
739 InvokeCallbackMethod
<SupportChaining::value
>(
740 mThisVal
.get(), mResolveMethod
, MaybeMove(aValue
.ResolveValue()),
741 std::move(mCompletionPromise
));
743 InvokeCallbackMethod
<SupportChaining::value
>(
744 mThisVal
.get(), mRejectMethod
, MaybeMove(aValue
.RejectValue()),
745 std::move(mCompletionPromise
));
748 // Null out mThisVal after invoking the callback so that any references
749 // are released predictably on the dispatch thread. Otherwise, it would be
750 // released on whatever thread last drops its reference to the ThenValue,
751 // which may or may not be ok.
757 mThisVal
; // Only accessed and refcounted on dispatch thread.
758 ResolveMethodType mResolveMethod
;
759 RejectMethodType mRejectMethod
;
760 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
763 template <typename ThisType
, typename ResolveRejectMethodType
>
764 class ThenValue
<ThisType
*, ResolveRejectMethodType
> : public ThenValueBase
{
765 friend class ThenCommand
<ThenValue
>;
767 using R1
= typename RemoveSmartPointer
<typename
detail::MethodTrait
<
768 ResolveRejectMethodType
>::ReturnType
>::Type
;
769 using SupportChaining
=
770 std::integral_constant
<bool, IsMozPromise
<R1
>::value
>;
772 // Fall back to MozPromise when promise chaining is not supported to make
775 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
778 ThenValue(nsISerialEventTarget
* aResponseTarget
, ThisType
* aThisVal
,
779 ResolveRejectMethodType aResolveRejectMethod
,
780 const char* aCallSite
)
781 : ThenValueBase(aResponseTarget
, aCallSite
),
783 mResolveRejectMethod(aResolveRejectMethod
) {}
785 void Disconnect() override
{
786 ThenValueBase::Disconnect();
788 // If a Request has been disconnected, we don't guarantee that the
789 // resolve/reject runnable will be dispatched. Null out our refcounted
790 // this-value now so that it's released predictably on the dispatch
796 MozPromiseBase
* CompletionPromise() const override
{
797 return mCompletionPromise
;
800 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
801 InvokeCallbackMethod
<SupportChaining::value
>(
802 mThisVal
.get(), mResolveRejectMethod
, MaybeMove(aValue
),
803 std::move(mCompletionPromise
));
805 // Null out mThisVal after invoking the callback so that any references
806 // are released predictably on the dispatch thread. Otherwise, it would be
807 // released on whatever thread last drops its reference to the ThenValue,
808 // which may or may not be ok.
814 mThisVal
; // Only accessed and refcounted on dispatch thread.
815 ResolveRejectMethodType mResolveRejectMethod
;
816 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
819 // NB: We could use std::function here instead of a template if it were
821 template <typename ResolveFunction
, typename RejectFunction
>
822 class ThenValue
<ResolveFunction
, RejectFunction
> : public ThenValueBase
{
823 friend class ThenCommand
<ThenValue
>;
825 using R1
= typename RemoveSmartPointer
<
826 typename
detail::MethodTrait
<ResolveFunction
>::ReturnType
>::Type
;
827 using R2
= typename RemoveSmartPointer
<
828 typename
detail::MethodTrait
<RejectFunction
>::ReturnType
>::Type
;
829 using SupportChaining
=
830 std::integral_constant
<bool, IsMozPromise
<R1
>::value
&&
831 std::is_same_v
<R1
, R2
>>;
833 // Fall back to MozPromise when promise chaining is not supported to make
836 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
839 ThenValue(nsISerialEventTarget
* aResponseTarget
,
840 ResolveFunction
&& aResolveFunction
,
841 RejectFunction
&& aRejectFunction
, const char* aCallSite
)
842 : ThenValueBase(aResponseTarget
, aCallSite
) {
843 mResolveFunction
.emplace(std::move(aResolveFunction
));
844 mRejectFunction
.emplace(std::move(aRejectFunction
));
847 void Disconnect() override
{
848 ThenValueBase::Disconnect();
850 // If a Request has been disconnected, we don't guarantee that the
851 // resolve/reject runnable will be dispatched. Destroy our callbacks
852 // now so that any references in closures are released predictable on
853 // the dispatch thread.
854 mResolveFunction
.reset();
855 mRejectFunction
.reset();
859 MozPromiseBase
* CompletionPromise() const override
{
860 return mCompletionPromise
;
863 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
864 // Note: The usage of InvokeCallbackMethod here requires that
865 // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
866 // classes with ::operator()), since it allows us to share code more
867 // easily. We could fix this if need be, though it's quite easy to work
868 // around by just capturing something.
869 if (aValue
.IsResolve()) {
870 InvokeCallbackMethod
<SupportChaining::value
>(
871 mResolveFunction
.ptr(), &ResolveFunction::operator(),
872 MaybeMove(aValue
.ResolveValue()), std::move(mCompletionPromise
));
874 InvokeCallbackMethod
<SupportChaining::value
>(
875 mRejectFunction
.ptr(), &RejectFunction::operator(),
876 MaybeMove(aValue
.RejectValue()), std::move(mCompletionPromise
));
879 // Destroy callbacks after invocation so that any references in closures
880 // are released predictably on the dispatch thread. Otherwise, they would
881 // be released on whatever thread last drops its reference to the
882 // ThenValue, which may or may not be ok.
883 mResolveFunction
.reset();
884 mRejectFunction
.reset();
888 Maybe
<ResolveFunction
>
889 mResolveFunction
; // Only accessed and deleted on dispatch thread.
890 Maybe
<RejectFunction
>
891 mRejectFunction
; // Only accessed and deleted on dispatch thread.
892 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
895 template <typename ResolveRejectFunction
>
896 class ThenValue
<ResolveRejectFunction
> : public ThenValueBase
{
897 friend class ThenCommand
<ThenValue
>;
899 using R1
= typename RemoveSmartPointer
<
900 typename
detail::MethodTrait
<ResolveRejectFunction
>::ReturnType
>::Type
;
901 using SupportChaining
=
902 std::integral_constant
<bool, IsMozPromise
<R1
>::value
>;
904 // Fall back to MozPromise when promise chaining is not supported to make
907 std::conditional_t
<SupportChaining::value
, R1
, MozPromise
>;
910 ThenValue(nsISerialEventTarget
* aResponseTarget
,
911 ResolveRejectFunction
&& aResolveRejectFunction
,
912 const char* aCallSite
)
913 : ThenValueBase(aResponseTarget
, aCallSite
) {
914 mResolveRejectFunction
.emplace(std::move(aResolveRejectFunction
));
917 void Disconnect() override
{
918 ThenValueBase::Disconnect();
920 // If a Request has been disconnected, we don't guarantee that the
921 // resolve/reject runnable will be dispatched. Destroy our callbacks
922 // now so that any references in closures are released predictable on
923 // the dispatch thread.
924 mResolveRejectFunction
.reset();
928 MozPromiseBase
* CompletionPromise() const override
{
929 return mCompletionPromise
;
932 void DoResolveOrRejectInternal(ResolveOrRejectValue
& aValue
) override
{
933 // Note: The usage of InvokeCallbackMethod here requires that
934 // ResolveRejectFunction is capture-lambdas (i.e. anonymous
935 // classes with ::operator()), since it allows us to share code more
936 // easily. We could fix this if need be, though it's quite easy to work
937 // around by just capturing something.
938 InvokeCallbackMethod
<SupportChaining::value
>(
939 mResolveRejectFunction
.ptr(), &ResolveRejectFunction::operator(),
940 MaybeMove(aValue
), std::move(mCompletionPromise
));
942 // Destroy callbacks after invocation so that any references in closures
943 // are released predictably on the dispatch thread. Otherwise, they would
944 // be released on whatever thread last drops its reference to the
945 // ThenValue, which may or may not be ok.
946 mResolveRejectFunction
.reset();
950 Maybe
<ResolveRejectFunction
>
951 mResolveRejectFunction
; // Only accessed and deleted on dispatch
953 RefPtr
<typename
PromiseType::Private
> mCompletionPromise
;
957 void ThenInternal(already_AddRefed
<ThenValueBase
> aThenValue
,
958 const char* aCallSite
) {
959 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
960 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
961 RefPtr
<ThenValueBase
> thenValue
= aThenValue
;
962 MutexAutoLock
lock(mMutex
);
963 MOZ_DIAGNOSTIC_ASSERT(
964 !IsExclusive
|| !mHaveRequest
,
965 "Using an exclusive promise in a non-exclusive fashion");
967 PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
968 aCallSite
, this, thenValue
.get(), (int)IsPending());
970 thenValue
->Dispatch(this);
972 mThenValues
.AppendElement(thenValue
.forget());
978 * A command object to store all information needed to make a request to
979 * the promise. This allows us to delay the request until further use is
980 * known (whether it is ->Then() again for more promise chaining or ->Track()
981 * to terminate chaining and issue the request).
983 * This allows a unified syntax for promise chaining and disconnection
984 * and feels more like its JS counterpart.
986 template <typename ThenValueType
>
988 // Allow Promise1::ThenCommand to access the private constructor,
989 // Promise2::ThenCommand(ThenCommand&&).
990 template <typename
, typename
, bool>
991 friend class MozPromise
;
993 using PromiseType
= typename
ThenValueType::PromiseType
;
994 using Private
= typename
PromiseType::Private
;
996 ThenCommand(const char* aCallSite
,
997 already_AddRefed
<ThenValueType
> aThenValue
,
998 MozPromise
* aReceiver
)
999 : mCallSite(aCallSite
), mThenValue(aThenValue
), mReceiver(aReceiver
) {}
1001 ThenCommand(ThenCommand
&& aOther
) = default;
1005 // Issue the request now if the return value of Then() is not used.
1007 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
1011 // Allow RefPtr<MozPromise> p = somePromise->Then();
1012 // p->Then(thread1, ...);
1013 // p->Then(thread2, ...);
1014 operator RefPtr
<PromiseType
>() {
1016 ThenValueType::SupportChaining::value
,
1017 "The resolve/reject callback needs to return a RefPtr<MozPromise> "
1018 "in order to do promise chaining.");
1020 // mCompletionPromise must be created before ThenInternal() to avoid race.
1022 new Private("<completion promise>", true /* aIsCompletionPromise */);
1023 mThenValue
->mCompletionPromise
= p
;
1024 // Note ThenInternal() might nullify mCompletionPromise before return.
1025 // So we need to return p instead of mCompletionPromise.
1026 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
1030 template <typename
... Ts
>
1031 auto Then(Ts
&&... aArgs
) -> decltype(std::declval
<PromiseType
>().Then(
1032 std::forward
<Ts
>(aArgs
)...)) {
1033 return static_cast<RefPtr
<PromiseType
>>(*this)->Then(
1034 std::forward
<Ts
>(aArgs
)...);
1037 void Track(MozPromiseRequestHolder
<MozPromise
>& aRequestHolder
) {
1038 aRequestHolder
.Track(do_AddRef(mThenValue
));
1039 mReceiver
->ThenInternal(mThenValue
.forget(), mCallSite
);
1042 // Allow calling ->Then() again for more promise chaining or ->Track() to
1043 // end chaining and track the request for future disconnection.
1044 ThenCommand
* operator->() { return this; }
1047 const char* mCallSite
;
1048 RefPtr
<ThenValueType
> mThenValue
;
1049 RefPtr
<MozPromise
> mReceiver
;
1053 template <typename ThisType
, typename
... Methods
,
1054 typename ThenValueType
= ThenValue
<ThisType
*, Methods
...>,
1055 typename ReturnType
= ThenCommand
<ThenValueType
>>
1056 ReturnType
Then(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
,
1057 ThisType
* aThisVal
, Methods
... aMethods
) {
1058 RefPtr
<ThenValueType
> thenValue
=
1059 new ThenValueType(aResponseTarget
, aThisVal
, aMethods
..., aCallSite
);
1060 return ReturnType(aCallSite
, thenValue
.forget(), this);
1063 template <typename
... Functions
,
1064 typename ThenValueType
= ThenValue
<Functions
...>,
1065 typename ReturnType
= ThenCommand
<ThenValueType
>>
1066 ReturnType
Then(nsISerialEventTarget
* aResponseTarget
, const char* aCallSite
,
1067 Functions
&&... aFunctions
) {
1068 RefPtr
<ThenValueType
> thenValue
=
1069 new ThenValueType(aResponseTarget
, std::move(aFunctions
)..., aCallSite
);
1070 return ReturnType(aCallSite
, thenValue
.forget(), this);
1073 void ChainTo(already_AddRefed
<Private
> aChainedPromise
,
1074 const char* aCallSite
) {
1075 MutexAutoLock
lock(mMutex
);
1076 MOZ_DIAGNOSTIC_ASSERT(
1077 !IsExclusive
|| !mHaveRequest
,
1078 "Using an exclusive promise in a non-exclusive fashion");
1079 mHaveRequest
= true;
1080 RefPtr
<Private
> chainedPromise
= aChainedPromise
;
1082 "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
1083 aCallSite
, this, chainedPromise
.get(), (int)IsPending());
1085 // We want to use the same type of dispatching method with the chained
1088 // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
1089 // at compilation time to ensure we're not triggering the static_assert in
1090 // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
1092 if (mUseDirectTaskDispatch
) {
1093 chainedPromise
->UseDirectTaskDispatch(aCallSite
);
1094 } else if constexpr (IsExclusive
) {
1095 if (mUseSynchronousTaskDispatch
) {
1096 chainedPromise
->UseSynchronousTaskDispatch(aCallSite
);
1099 chainedPromise
->SetTaskPriority(mPriority
, aCallSite
);
1103 ForwardTo(chainedPromise
);
1105 mChainedPromises
.AppendElement(chainedPromise
);
1109 # ifdef MOZ_WIDGET_ANDROID
1110 // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
1111 [[nodiscard
]] static RefPtr
<MozPromise
> FromGeckoResult(
1112 java::GeckoResult::Param aGeckoResult
) {
1113 using jni::GeckoResultCallback
;
1114 RefPtr
<Private
> p
= new Private("GeckoResult Glue", false);
1115 auto resolve
= GeckoResultCallback::CreateAndAttach
<ResolveValueType
>(
1116 [p
](ResolveValueType
&& aArg
) {
1117 p
->Resolve(MaybeMove(aArg
), __func__
);
1119 auto reject
= GeckoResultCallback::CreateAndAttach
<RejectValueType
>(
1120 [p
](RejectValueType
&& aArg
) { p
->Reject(MaybeMove(aArg
), __func__
); });
1121 aGeckoResult
->NativeThen(resolve
, reject
);
1126 // Note we expose the function AssertIsDead() instead of IsDead() since
1127 // checking IsDead() is a data race in the situation where the request is not
1128 // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1129 // AssertIsDead() only.
1130 void AssertIsDead() override
{
1131 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1132 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1133 MutexAutoLock
lock(mMutex
);
1134 for (auto&& then
: mThenValues
) {
1135 then
->AssertIsDead();
1137 for (auto&& chained
: mChainedPromises
) {
1138 chained
->AssertIsDead();
1142 bool IsResolved() const { return mValue
.IsResolve(); }
1145 bool IsPending() const { return mValue
.IsNothing(); }
1147 ResolveOrRejectValue
& Value() {
1148 // This method should only be called once the value has stabilized. As
1149 // such, we don't need to acquire the lock here.
1150 MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1154 void DispatchAll() {
1155 mMutex
.AssertCurrentThreadOwns();
1156 for (auto&& thenValue
: mThenValues
) {
1157 thenValue
->Dispatch(this);
1159 mThenValues
.Clear();
1161 for (auto&& chainedPromise
: mChainedPromises
) {
1162 ForwardTo(chainedPromise
);
1164 mChainedPromises
.Clear();
1167 void ForwardTo(Private
* aOther
) {
1168 MOZ_ASSERT(!IsPending());
1169 if (mValue
.IsResolve()) {
1170 aOther
->Resolve(MaybeMove(mValue
.ResolveValue()), "<chained promise>");
1172 aOther
->Reject(MaybeMove(mValue
.RejectValue()), "<chained promise>");
1176 virtual ~MozPromise() {
1177 PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1179 // We can't guarantee a completion promise will always be revolved or
1180 // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1181 if (!mIsCompletionPromise
) {
1182 MOZ_ASSERT(!IsPending());
1183 MOZ_ASSERT(mThenValues
.IsEmpty());
1184 MOZ_ASSERT(mChainedPromises
.IsEmpty());
1186 # ifdef PROMISE_DEBUG
1194 const char* mCreationSite
; // For logging
1195 Mutex mMutex MOZ_UNANNOTATED
;
1196 ResolveOrRejectValue mValue
;
1197 bool mUseSynchronousTaskDispatch
= false;
1198 bool mUseDirectTaskDispatch
= false;
1199 uint32_t mPriority
= nsIRunnablePriority::PRIORITY_NORMAL
;
1200 # ifdef PROMISE_DEBUG
1201 uint32_t mMagic1
= sMagic
;
1203 // Try shows we never have more than 3 elements when IsExclusive is false.
1204 // So '3' is a good value to avoid heap allocation in most cases.
1205 AutoTArray
<RefPtr
<ThenValueBase
>, IsExclusive
? 1 : 3> mThenValues
;
1206 # ifdef PROMISE_DEBUG
1207 uint32_t mMagic2
= sMagic
;
1209 nsTArray
<RefPtr
<Private
>> mChainedPromises
;
1210 # ifdef PROMISE_DEBUG
1211 uint32_t mMagic3
= sMagic
;
1214 const bool mIsCompletionPromise
;
1215 # ifdef PROMISE_DEBUG
1220 template <typename ResolveValueT
, typename RejectValueT
, bool IsExclusive
>
1221 class MozPromise
<ResolveValueT
, RejectValueT
, IsExclusive
>::Private
1222 : public MozPromise
<ResolveValueT
, RejectValueT
, IsExclusive
> {
1224 explicit Private(const char* aCreationSite
, bool aIsCompletionPromise
= false)
1225 : MozPromise(aCreationSite
, aIsCompletionPromise
) {}
1227 template <typename ResolveValueT_
>
1228 void Resolve(ResolveValueT_
&& aResolveValue
, const char* aResolveSite
) {
1229 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1230 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1231 MutexAutoLock
lock(mMutex
);
1232 PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite
,
1233 this, mCreationSite
);
1236 "%s ignored already resolved or rejected MozPromise (%p created at "
1238 aResolveSite
, this, mCreationSite
);
1241 mValue
.SetResolve(std::forward
<ResolveValueT_
>(aResolveValue
));
1245 template <typename RejectValueT_
>
1246 void Reject(RejectValueT_
&& aRejectValue
, const char* aRejectSite
) {
1247 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1248 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1249 MutexAutoLock
lock(mMutex
);
1250 PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite
, this,
1254 "%s ignored already resolved or rejected MozPromise (%p created at "
1256 aRejectSite
, this, mCreationSite
);
1259 mValue
.SetReject(std::forward
<RejectValueT_
>(aRejectValue
));
1263 template <typename ResolveOrRejectValue_
>
1264 void ResolveOrReject(ResolveOrRejectValue_
&& aValue
, const char* aSite
) {
1265 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1266 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1267 MutexAutoLock
lock(mMutex
);
1268 PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite
,
1269 this, mCreationSite
);
1272 "%s ignored already resolved or rejected MozPromise (%p created at "
1274 aSite
, this, mCreationSite
);
1277 mValue
= std::forward
<ResolveOrRejectValue_
>(aValue
);
1281 // If the caller and target are both on the same thread, run the the resolve
1282 // or reject callback synchronously. Otherwise, the task will be dispatched
1283 // via the target Dispatch method.
1284 void UseSynchronousTaskDispatch(const char* aSite
) {
1287 "Synchronous dispatch can only be used with exclusive promises");
1288 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1289 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1290 MutexAutoLock
lock(mMutex
);
1291 PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
1292 aSite
, this, mCreationSite
);
1293 MOZ_ASSERT(IsPending(),
1294 "A Promise must not have been already resolved or rejected to "
1295 "set dispatch state");
1296 mUseSynchronousTaskDispatch
= true;
1299 // If the caller and target are both on the same thread, run the
1300 // resolve/reject callback off the direct task queue instead. This avoids a
1301 // full trip to the back of the event queue for each additional asynchronous
1302 // step when using MozPromise, and is similar (but not identical to) the
1303 // microtask semantics of JS promises.
1304 void UseDirectTaskDispatch(const char* aSite
) {
1305 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1306 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1307 MutexAutoLock
lock(mMutex
);
1308 PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite
,
1309 this, mCreationSite
);
1310 MOZ_ASSERT(IsPending(),
1311 "A Promise must not have been already resolved or rejected to "
1312 "set dispatch state");
1313 MOZ_ASSERT(!mUseSynchronousTaskDispatch
,
1314 "Promise already set for synchronous dispatch");
1315 mUseDirectTaskDispatch
= true;
1318 // If the resolve/reject will be handled on a thread supporting priorities,
1319 // one may want to tweak the priority of the task by passing a
1320 // nsIRunnablePriority::PRIORITY_* to SetTaskPriority.
1321 void SetTaskPriority(uint32_t aPriority
, const char* aSite
) {
1322 PROMISE_ASSERT(mMagic1
== sMagic
&& mMagic2
== sMagic
&&
1323 mMagic3
== sMagic
&& mMagic4
== &mMutex
);
1324 MutexAutoLock
lock(mMutex
);
1325 PROMISE_LOG("%s TaskPriority MozPromise (%p created at %s)", aSite
, this,
1327 MOZ_ASSERT(IsPending(),
1328 "A Promise must not have been already resolved or rejected to "
1329 "set dispatch state");
1330 MOZ_ASSERT(!mUseSynchronousTaskDispatch
,
1331 "Promise already set for synchronous dispatch");
1332 MOZ_ASSERT(!mUseDirectTaskDispatch
,
1333 "Promise already set for direct dispatch");
1334 mPriority
= aPriority
;
1338 // A generic promise type that does the trick for simple use cases.
1339 typedef MozPromise
<bool, nsresult
, /* IsExclusive = */ true> GenericPromise
;
1341 // A generic, non-exclusive promise type that does the trick for simple use
1343 typedef MozPromise
<bool, nsresult
, /* IsExclusive = */ false>
1344 GenericNonExclusivePromise
;
1347 * Class to encapsulate a promise for a particular role. Use this as the member
1348 * variable for a class whose method returns a promise.
1350 template <typename PromiseType
, typename ImplType
>
1351 class MozPromiseHolderBase
{
1353 MozPromiseHolderBase() = default;
1355 MozPromiseHolderBase(MozPromiseHolderBase
&& aOther
) = default;
1356 MozPromiseHolderBase
& operator=(MozPromiseHolderBase
&& aOther
) = default;
1358 ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise
); }
1360 already_AddRefed
<PromiseType
> Ensure(const char* aMethodName
) {
1361 static_cast<ImplType
*>(this)->Check();
1363 mPromise
= new (typename
PromiseType::Private
)(aMethodName
);
1365 RefPtr
<PromiseType
> p
= mPromise
.get();
1369 bool IsEmpty() const {
1370 static_cast<const ImplType
*>(this)->Check();
1374 already_AddRefed
<typename
PromiseType::Private
> Steal() {
1375 static_cast<ImplType
*>(this)->Check();
1376 return mPromise
.forget();
1379 template <typename ResolveValueType_
>
1380 void Resolve(ResolveValueType_
&& aResolveValue
, const char* aMethodName
) {
1381 static_assert(std::is_convertible_v
<ResolveValueType_
,
1382 typename
PromiseType::ResolveValueType
>,
1383 "Resolve() argument must be implicitly convertible to "
1384 "MozPromise's ResolveValueT");
1386 static_cast<ImplType
*>(this)->Check();
1387 MOZ_ASSERT(mPromise
);
1388 mPromise
->Resolve(std::forward
<ResolveValueType_
>(aResolveValue
),
1393 template <typename ResolveValueType_
>
1394 void ResolveIfExists(ResolveValueType_
&& aResolveValue
,
1395 const char* aMethodName
) {
1397 Resolve(std::forward
<ResolveValueType_
>(aResolveValue
), aMethodName
);
1401 template <typename RejectValueType_
>
1402 void Reject(RejectValueType_
&& aRejectValue
, const char* aMethodName
) {
1403 static_assert(std::is_convertible_v
<RejectValueType_
,
1404 typename
PromiseType::RejectValueType
>,
1405 "Reject() argument must be implicitly convertible to "
1406 "MozPromise's RejectValueT");
1408 static_cast<ImplType
*>(this)->Check();
1409 MOZ_ASSERT(mPromise
);
1410 mPromise
->Reject(std::forward
<RejectValueType_
>(aRejectValue
), aMethodName
);
1414 template <typename RejectValueType_
>
1415 void RejectIfExists(RejectValueType_
&& aRejectValue
,
1416 const char* aMethodName
) {
1418 Reject(std::forward
<RejectValueType_
>(aRejectValue
), aMethodName
);
1422 template <typename ResolveOrRejectValueType_
>
1423 void ResolveOrReject(ResolveOrRejectValueType_
&& aValue
,
1424 const char* aMethodName
) {
1425 static_cast<ImplType
*>(this)->Check();
1426 MOZ_ASSERT(mPromise
);
1427 mPromise
->ResolveOrReject(std::forward
<ResolveOrRejectValueType_
>(aValue
),
1432 template <typename ResolveOrRejectValueType_
>
1433 void ResolveOrRejectIfExists(ResolveOrRejectValueType_
&& aValue
,
1434 const char* aMethodName
) {
1436 ResolveOrReject(std::forward
<ResolveOrRejectValueType_
>(aValue
),
1441 void UseSynchronousTaskDispatch(const char* aSite
) {
1442 MOZ_ASSERT(mPromise
);
1443 mPromise
->UseSynchronousTaskDispatch(aSite
);
1446 void UseDirectTaskDispatch(const char* aSite
) {
1447 MOZ_ASSERT(mPromise
);
1448 mPromise
->UseDirectTaskDispatch(aSite
);
1451 void SetTaskPriority(uint32_t aPriority
, const char* aSite
) {
1452 MOZ_ASSERT(mPromise
);
1453 mPromise
->SetTaskPriority(aPriority
, aSite
);
1457 RefPtr
<typename
PromiseType::Private
> mPromise
;
1460 template <typename PromiseType
>
1461 class MozPromiseHolder
1462 : public MozPromiseHolderBase
<PromiseType
, MozPromiseHolder
<PromiseType
>> {
1464 using MozPromiseHolderBase
<
1465 PromiseType
, MozPromiseHolder
<PromiseType
>>::MozPromiseHolderBase
;
1466 static constexpr void Check(){};
1469 template <typename PromiseType
>
1470 class MozMonitoredPromiseHolder
1471 : public MozPromiseHolderBase
<PromiseType
,
1472 MozMonitoredPromiseHolder
<PromiseType
>> {
1474 // Provide a Monitor that should always be held when accessing this instance.
1475 explicit MozMonitoredPromiseHolder(Monitor
* const aMonitor
)
1476 : mMonitor(aMonitor
) {
1477 MOZ_ASSERT(aMonitor
);
1480 MozMonitoredPromiseHolder(MozMonitoredPromiseHolder
&& aOther
) = delete;
1481 MozMonitoredPromiseHolder
& operator=(MozMonitoredPromiseHolder
&& aOther
) =
1484 void Check() const { mMonitor
->AssertCurrentThreadOwns(); }
1487 Monitor
* const mMonitor
;
1491 * Class to encapsulate a MozPromise::Request reference. Use this as the member
1492 * variable for a class waiting on a MozPromise.
1494 template <typename PromiseType
>
1495 class MozPromiseRequestHolder
{
1497 MozPromiseRequestHolder() = default;
1498 ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest
); }
1500 void Track(already_AddRefed
<typename
PromiseType::Request
> aRequest
) {
1501 MOZ_DIAGNOSTIC_ASSERT(!Exists());
1502 mRequest
= aRequest
;
1506 MOZ_DIAGNOSTIC_ASSERT(Exists());
1510 // Disconnects and forgets an outstanding promise. The resolve/reject methods
1511 // will never be called.
1513 MOZ_ASSERT(Exists());
1514 mRequest
->Disconnect();
1518 void DisconnectIfExists() {
1524 bool Exists() const { return !!mRequest
; }
1527 RefPtr
<typename
PromiseType::Request
> mRequest
;
1530 // Asynchronous Potentially-Cross-Thread Method Calls.
1532 // This machinery allows callers to schedule a promise-returning function
1533 // (a method and object, or a function object like a lambda) to be invoked
1534 // asynchronously on a given thread, while at the same time receiving a
1535 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1536 // task to invoke the function on the proper thread and also chain the
1537 // resulting promise to the one that the caller received, so that resolve/
1538 // reject values are forwarded through.
1542 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1543 // assertions when used on templated types.
1544 class MethodCallBase
{
1546 MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase
)
1547 MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase
)
1550 template <typename PromiseType
, typename MethodType
, typename ThisType
,
1551 typename
... Storages
>
1552 class MethodCall
: public MethodCallBase
{
1554 template <typename
... Args
>
1555 MethodCall(MethodType aMethod
, ThisType
* aThisVal
, Args
&&... aArgs
)
1558 mArgs(std::forward
<Args
>(aArgs
)...) {
1559 static_assert(sizeof...(Storages
) == sizeof...(Args
),
1560 "Storages and Args should have equal sizes");
1563 RefPtr
<PromiseType
> Invoke() { return mArgs
.apply(mThisVal
.get(), mMethod
); }
1567 RefPtr
<ThisType
> mThisVal
;
1568 RunnableMethodArguments
<Storages
...> mArgs
;
1571 template <typename PromiseType
, typename MethodType
, typename ThisType
,
1572 typename
... Storages
>
1573 class ProxyRunnable
: public CancelableRunnable
{
1576 typename
PromiseType::Private
* aProxyPromise
,
1577 MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>* aMethodCall
)
1578 : CancelableRunnable("detail::ProxyRunnable"),
1579 mProxyPromise(aProxyPromise
),
1580 mMethodCall(aMethodCall
) {}
1582 NS_IMETHOD
Run() override
{
1583 RefPtr
<PromiseType
> p
= mMethodCall
->Invoke();
1584 mMethodCall
= nullptr;
1585 p
->ChainTo(mProxyPromise
.forget(), "<Proxy Promise>");
1589 nsresult
Cancel() override
{ return Run(); }
1592 RefPtr
<typename
PromiseType::Private
> mProxyPromise
;
1593 UniquePtr
<MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>>
1597 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1598 typename
... ArgTypes
, typename
... ActualArgTypes
>
1599 static RefPtr
<PromiseType
> InvokeAsyncImpl(
1600 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1601 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1602 ActualArgTypes
&&... aArgs
) {
1603 MOZ_ASSERT(aTarget
);
1605 typedef RefPtr
<PromiseType
> (ThisType::*MethodType
)(ArgTypes
...);
1606 typedef detail::MethodCall
<PromiseType
, MethodType
, ThisType
, Storages
...>
1608 typedef detail::ProxyRunnable
<PromiseType
, MethodType
, ThisType
, Storages
...>
1611 MethodCallType
* methodCall
= new MethodCallType(
1612 aMethod
, aThisVal
, std::forward
<ActualArgTypes
>(aArgs
)...);
1613 RefPtr
<typename
PromiseType::Private
> p
=
1614 new (typename
PromiseType::Private
)(aCallerName
);
1615 RefPtr
<ProxyRunnableType
> r
= new ProxyRunnableType(p
, methodCall
);
1616 aTarget
->Dispatch(r
.forget());
1620 constexpr bool Any() { return false; }
1622 template <typename T1
>
1623 constexpr bool Any(T1 a
) {
1624 return static_cast<bool>(a
);
1627 template <typename T1
, typename
... Ts
>
1628 constexpr bool Any(T1 a
, Ts
... aOthers
) {
1629 return a
|| Any(aOthers
...);
1632 } // namespace detail
1634 // InvokeAsync with explicitly-specified storages.
1635 // See ParameterStorage in nsThreadUtils.h for help.
1636 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1637 typename
... ArgTypes
, typename
... ActualArgTypes
,
1638 std::enable_if_t
<sizeof...(Storages
) != 0, int> = 0>
1639 static RefPtr
<PromiseType
> InvokeAsync(
1640 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1641 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1642 ActualArgTypes
&&... aArgs
) {
1644 sizeof...(Storages
) == sizeof...(ArgTypes
),
1645 "Provided Storages and method's ArgTypes should have equal sizes");
1646 static_assert(sizeof...(Storages
) == sizeof...(ActualArgTypes
),
1647 "Provided Storages and ActualArgTypes should have equal sizes");
1648 return detail::InvokeAsyncImpl
<Storages
...>(
1649 aTarget
, aThisVal
, aCallerName
, aMethod
,
1650 std::forward
<ActualArgTypes
>(aArgs
)...);
1653 // InvokeAsync with no explicitly-specified storages, will copy arguments and
1654 // then move them out of the runnable into the target method parameters.
1655 template <typename
... Storages
, typename PromiseType
, typename ThisType
,
1656 typename
... ArgTypes
, typename
... ActualArgTypes
,
1657 std::enable_if_t
<sizeof...(Storages
) == 0, int> = 0>
1658 static RefPtr
<PromiseType
> InvokeAsync(
1659 nsISerialEventTarget
* aTarget
, ThisType
* aThisVal
, const char* aCallerName
,
1660 RefPtr
<PromiseType
> (ThisType::*aMethod
)(ArgTypes
...),
1661 ActualArgTypes
&&... aArgs
) {
1664 std::is_pointer_v
<std::remove_reference_t
<ActualArgTypes
>>...),
1665 "Cannot pass pointer types through InvokeAsync, Storages must be "
1667 static_assert(sizeof...(ArgTypes
) == sizeof...(ActualArgTypes
),
1668 "Method's ArgTypes and ActualArgTypes should have equal sizes");
1669 return detail::InvokeAsyncImpl
<
1670 StoreCopyPassByRRef
<std::decay_t
<ActualArgTypes
>>...>(
1671 aTarget
, aThisVal
, aCallerName
, aMethod
,
1672 std::forward
<ActualArgTypes
>(aArgs
)...);
1677 template <typename Function
, typename PromiseType
>
1678 class ProxyFunctionRunnable
: public CancelableRunnable
{
1679 using FunctionStorage
= std::decay_t
<Function
>;
1682 template <typename F
>
1683 ProxyFunctionRunnable(typename
PromiseType::Private
* aProxyPromise
,
1685 : CancelableRunnable("detail::ProxyFunctionRunnable"),
1686 mProxyPromise(aProxyPromise
),
1687 mFunction(new FunctionStorage(std::forward
<F
>(aFunction
))) {}
1689 NS_IMETHOD
Run() override
{
1690 RefPtr
<PromiseType
> p
= (*mFunction
)();
1691 mFunction
= nullptr;
1692 p
->ChainTo(mProxyPromise
.forget(), "<Proxy Promise>");
1696 nsresult
Cancel() override
{ return Run(); }
1699 RefPtr
<typename
PromiseType::Private
> mProxyPromise
;
1700 UniquePtr
<FunctionStorage
> mFunction
;
1703 // Note: The following struct and function are not for public consumption (yet?)
1704 // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1705 // function objects). They could be moved outside of detail if really needed.
1707 // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1708 // and their captures). This struct is a tag that allows the use of objects
1709 // through lvalue-refs where necessary.
1710 struct AllowInvokeAsyncFunctionLVRef
{};
1712 // Invoke a function object (e.g., lambda or std/mozilla::function)
1713 // asynchronously; note that the object will be copied if provided by
1714 // lvalue-ref. Return a promise that the function should eventually resolve or
1716 template <typename Function
>
1717 static auto InvokeAsync(nsISerialEventTarget
* aTarget
, const char* aCallerName
,
1718 AllowInvokeAsyncFunctionLVRef
, Function
&& aFunction
)
1719 -> decltype(aFunction()) {
1721 IsRefcountedSmartPointer
<decltype(aFunction())>::value
&&
1723 typename RemoveSmartPointer
<decltype(aFunction())>::Type
>::value
,
1724 "Function object must return RefPtr<MozPromise>");
1725 MOZ_ASSERT(aTarget
);
1726 typedef typename RemoveSmartPointer
<decltype(aFunction())>::Type PromiseType
;
1727 typedef detail::ProxyFunctionRunnable
<Function
, PromiseType
>
1730 auto p
= MakeRefPtr
<typename
PromiseType::Private
>(aCallerName
);
1731 auto r
= MakeRefPtr
<ProxyRunnableType
>(p
, std::forward
<Function
>(aFunction
));
1732 aTarget
->Dispatch(r
.forget());
1736 } // namespace detail
1738 // Invoke a function object (e.g., lambda) asynchronously.
1739 // Return a promise that the function should eventually resolve or reject.
1740 template <typename Function
>
1741 static auto InvokeAsync(nsISerialEventTarget
* aTarget
, const char* aCallerName
,
1742 Function
&& aFunction
) -> decltype(aFunction()) {
1743 static_assert(!std::is_lvalue_reference_v
<Function
>,
1744 "Function object must not be passed by lvalue-ref (to avoid "
1745 "unplanned copies); Consider move()ing the object.");
1746 return detail::InvokeAsync(aTarget
, aCallerName
,
1747 detail::AllowInvokeAsyncFunctionLVRef(),
1748 std::forward
<Function
>(aFunction
));
1752 # undef PROMISE_ASSERT
1753 # undef PROMISE_DEBUG
1755 } // namespace mozilla