Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / MozPromise.h
blobdbd05c1b17ff540eac4b0f87ad32f2fe3a4535ee
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_)
8 # define MozPromise_h_
10 # include <type_traits>
11 # include <utility>
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/UniquePtr.h"
19 # include "mozilla/Variant.h"
20 # include "nsIDirectTaskDispatcher.h"
21 # include "nsISerialEventTarget.h"
22 # include "nsTArray.h"
23 # include "nsThreadUtils.h"
25 # ifdef MOZ_WIDGET_ANDROID
26 # include "mozilla/jni/GeckoResultUtils.h"
27 # endif
29 # if MOZ_DIAGNOSTIC_ASSERT_ENABLED
30 # define PROMISE_DEBUG
31 # endif
33 # ifdef PROMISE_DEBUG
34 # define PROMISE_ASSERT MOZ_RELEASE_ASSERT
35 # else
36 # define PROMISE_ASSERT(...) \
37 do { \
38 } while (0)
39 # endif
41 # if DEBUG
42 # include "nsPrintfCString.h"
43 # endif
45 namespace mozilla {
47 namespace dom {
48 class Promise;
51 extern LazyLogModule gMozPromiseLog;
53 # define PROMISE_LOG(x, ...) \
54 MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
56 namespace detail {
57 template <typename F>
58 struct MethodTraitsHelper : MethodTraitsHelper<decltype(&F::operator())> {};
59 template <typename ThisType, typename Ret, typename... ArgTypes>
60 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...)> {
61 using ReturnType = Ret;
62 static const size_t ArgSize = sizeof...(ArgTypes);
64 template <typename ThisType, typename Ret, typename... ArgTypes>
65 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const> {
66 using ReturnType = Ret;
67 static const size_t ArgSize = sizeof...(ArgTypes);
69 template <typename ThisType, typename Ret, typename... ArgTypes>
70 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) volatile> {
71 using ReturnType = Ret;
72 static const size_t ArgSize = sizeof...(ArgTypes);
74 template <typename ThisType, typename Ret, typename... ArgTypes>
75 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const volatile> {
76 using ReturnType = Ret;
77 static const size_t ArgSize = sizeof...(ArgTypes);
79 template <typename T>
80 struct MethodTrait : MethodTraitsHelper<std::remove_reference_t<T>> {};
82 } // namespace detail
84 template <typename MethodType>
85 using TakesArgument =
86 std::integral_constant<bool, detail::MethodTrait<MethodType>::ArgSize != 0>;
88 template <typename MethodType, typename TargetType>
89 using ReturnTypeIs =
90 std::is_convertible<typename detail::MethodTrait<MethodType>::ReturnType,
91 TargetType>;
93 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
94 class MozPromise;
96 template <typename Return>
97 struct IsMozPromise : std::false_type {};
99 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
100 struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
101 : std::true_type {};
104 * A promise manages an asynchronous request that may or may not be able to be
105 * fulfilled immediately. When an API returns a promise, the consumer may attach
106 * callbacks to be invoked (asynchronously, on a specified thread) when the
107 * request is either completed (resolved) or cannot be completed (rejected).
108 * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
109 * MozPromises resolution/rejection make a normal round-trip through the event
110 * loop, which simplifies their ordering semantics relative to other native
111 * code.
113 * MozPromises attempt to mirror the spirit of JS Promises to the extent that
114 * is possible (and desirable) in C++. While the intent is that MozPromises
115 * feel familiar to programmers who are accustomed to their JS-implemented
116 * cousin, we don't shy away from imposing restrictions and adding features that
117 * make sense for the use cases we encounter.
119 * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
120 * call accepts resolve and reject callbacks, and returns a magic object which
121 * will be implicitly converted to a MozPromise::Request or a MozPromise object
122 * depending on how the return value is used. The magic object serves several
123 * purposes for the consumer.
125 * (1) When converting to a MozPromise::Request, it allows the caller to
126 * cancel the delivery of the resolve/reject value if it has not already
127 * occurred, via Disconnect() (this must be done on the target thread to
128 * avoid racing).
130 * (2) When converting to a MozPromise (which is called a completion promise),
131 * it allows promise chaining so ->Then() can be called again to attach
132 * more resolve and reject callbacks. If the resolve/reject callback
133 * returns a new MozPromise, that promise is chained to the completion
134 * promise, such that its resolve/reject value will be forwarded along
135 * when it arrives. If the resolve/reject callback returns void, the
136 * completion promise is resolved/rejected with the same value that was
137 * passed to the callback.
139 * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
140 * (rather than already_AddRefed) from various methods. This is done to allow
141 * elegant chaining of calls without cluttering up the code with intermediate
142 * variables, and without introducing separate API variants for callers that
143 * want a return value (from, say, ->Then()) from those that don't.
145 * When IsExclusive is true, the MozPromise does a release-mode assertion that
146 * there is at most one call to either Then(...) or ChainTo(...).
149 class MozPromiseRefcountable {
150 public:
151 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
152 protected:
153 virtual ~MozPromiseRefcountable() = default;
156 class MozPromiseBase : public MozPromiseRefcountable {
157 public:
158 virtual void AssertIsDead() = 0;
161 template <typename T>
162 class MozPromiseHolder;
163 template <typename T>
164 class MozPromiseRequestHolder;
165 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
166 class MozPromise : public MozPromiseBase {
167 static const uint32_t sMagic = 0xcecace11;
169 // Return a |T&&| to enable move when IsExclusive is true or
170 // a |const T&| to enforce copy otherwise.
171 template <typename T,
172 typename R = std::conditional_t<IsExclusive, T&&, const T&>>
173 static R MaybeMove(T& aX) {
174 return static_cast<R>(aX);
177 public:
178 typedef ResolveValueT ResolveValueType;
179 typedef RejectValueT RejectValueType;
180 class ResolveOrRejectValue {
181 public:
182 template <typename ResolveValueType_>
183 void SetResolve(ResolveValueType_&& aResolveValue) {
184 MOZ_ASSERT(IsNothing());
185 mValue = Storage(VariantIndex<ResolveIndex>{},
186 std::forward<ResolveValueType_>(aResolveValue));
189 template <typename RejectValueType_>
190 void SetReject(RejectValueType_&& aRejectValue) {
191 MOZ_ASSERT(IsNothing());
192 mValue = Storage(VariantIndex<RejectIndex>{},
193 std::forward<RejectValueType_>(aRejectValue));
196 template <typename ResolveValueType_>
197 static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue) {
198 ResolveOrRejectValue val;
199 val.SetResolve(std::forward<ResolveValueType_>(aResolveValue));
200 return val;
203 template <typename RejectValueType_>
204 static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue) {
205 ResolveOrRejectValue val;
206 val.SetReject(std::forward<RejectValueType_>(aRejectValue));
207 return val;
210 bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
211 bool IsReject() const { return mValue.template is<RejectIndex>(); }
212 bool IsNothing() const { return mValue.template is<NothingIndex>(); }
214 const ResolveValueType& ResolveValue() const {
215 return mValue.template as<ResolveIndex>();
217 ResolveValueType& ResolveValue() {
218 return mValue.template as<ResolveIndex>();
220 const RejectValueType& RejectValue() const {
221 return mValue.template as<RejectIndex>();
223 RejectValueType& RejectValue() { return mValue.template as<RejectIndex>(); }
225 private:
226 enum { NothingIndex, ResolveIndex, RejectIndex };
227 using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
228 Storage mValue = Storage(VariantIndex<NothingIndex>{});
231 protected:
232 // MozPromise is the public type, and never constructed directly. Construct
233 // a MozPromise::Private, defined below.
234 MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
235 : mCreationSite(aCreationSite),
236 mMutex("MozPromise Mutex"),
237 mHaveRequest(false),
238 mIsCompletionPromise(aIsCompletionPromise)
239 # ifdef PROMISE_DEBUG
241 mMagic4(&mMutex)
242 # endif
244 PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
247 public:
248 // MozPromise::Private allows us to separate the public interface (upon which
249 // consumers of the promise may invoke methods like Then()) from the private
250 // interface (upon which the creator of the promise may invoke Resolve() or
251 // Reject()). APIs should create and store a MozPromise::Private (usually
252 // via a MozPromiseHolder), and return a MozPromise to consumers.
254 // NB: We can include the definition of this class inline once B2G ICS is
255 // gone.
256 class Private;
258 template <typename ResolveValueType_>
259 [[nodiscard]] static RefPtr<MozPromise> CreateAndResolve(
260 ResolveValueType_&& aResolveValue, const char* aResolveSite) {
261 static_assert(std::is_convertible_v<ResolveValueType_, ResolveValueT>,
262 "Resolve() argument must be implicitly convertible to "
263 "MozPromise's ResolveValueT");
264 RefPtr<typename MozPromise::Private> p =
265 new MozPromise::Private(aResolveSite);
266 p->Resolve(std::forward<ResolveValueType_>(aResolveValue), aResolveSite);
267 return p;
270 template <typename RejectValueType_>
271 [[nodiscard]] static RefPtr<MozPromise> CreateAndReject(
272 RejectValueType_&& aRejectValue, const char* aRejectSite) {
273 static_assert(std::is_convertible_v<RejectValueType_, RejectValueT>,
274 "Reject() argument must be implicitly convertible to "
275 "MozPromise's RejectValueT");
276 RefPtr<typename MozPromise::Private> p =
277 new MozPromise::Private(aRejectSite);
278 p->Reject(std::forward<RejectValueType_>(aRejectValue), aRejectSite);
279 return p;
282 template <typename ResolveOrRejectValueType_>
283 [[nodiscard]] static RefPtr<MozPromise> CreateAndResolveOrReject(
284 ResolveOrRejectValueType_&& aValue, const char* aSite) {
285 RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aSite);
286 p->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue), aSite);
287 return p;
290 typedef MozPromise<CopyableTArray<ResolveValueType>, RejectValueType,
291 IsExclusive>
292 AllPromiseType;
294 typedef MozPromise<CopyableTArray<ResolveOrRejectValue>, bool, IsExclusive>
295 AllSettledPromiseType;
297 private:
298 class AllPromiseHolder : public MozPromiseRefcountable {
299 public:
300 explicit AllPromiseHolder(size_t aDependentPromises)
301 : mPromise(new typename AllPromiseType::Private(__func__)),
302 mOutstandingPromises(aDependentPromises) {
303 MOZ_ASSERT(aDependentPromises > 0);
304 mResolveValues.SetLength(aDependentPromises);
307 template <typename ResolveValueType_>
308 void Resolve(size_t aIndex, ResolveValueType_&& aResolveValue) {
309 if (!mPromise) {
310 // Already rejected.
311 return;
314 mResolveValues[aIndex].emplace(
315 std::forward<ResolveValueType_>(aResolveValue));
316 if (--mOutstandingPromises == 0) {
317 nsTArray<ResolveValueType> resolveValues;
318 resolveValues.SetCapacity(mResolveValues.Length());
319 for (auto&& resolveValue : mResolveValues) {
320 resolveValues.AppendElement(std::move(resolveValue.ref()));
323 mPromise->Resolve(std::move(resolveValues), __func__);
324 mPromise = nullptr;
325 mResolveValues.Clear();
329 template <typename RejectValueType_>
330 void Reject(RejectValueType_&& aRejectValue) {
331 if (!mPromise) {
332 // Already rejected.
333 return;
336 mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), __func__);
337 mPromise = nullptr;
338 mResolveValues.Clear();
341 AllPromiseType* Promise() { return mPromise; }
343 private:
344 nsTArray<Maybe<ResolveValueType>> mResolveValues;
345 RefPtr<typename AllPromiseType::Private> mPromise;
346 size_t mOutstandingPromises;
349 // Trying to pass ResolveOrRejectValue by value fails static analysis checks,
350 // so we need to use either a const& or an rvalue reference, depending on
351 // whether IsExclusive is true or not.
352 typedef std::conditional_t<IsExclusive, ResolveOrRejectValue&&,
353 const ResolveOrRejectValue&>
354 ResolveOrRejectValueParam;
356 typedef std::conditional_t<IsExclusive, ResolveValueType&&,
357 const ResolveValueType&>
358 ResolveValueTypeParam;
360 typedef std::conditional_t<IsExclusive, RejectValueType&&,
361 const RejectValueType&>
362 RejectValueTypeParam;
364 class AllSettledPromiseHolder : public MozPromiseRefcountable {
365 public:
366 explicit AllSettledPromiseHolder(size_t aDependentPromises)
367 : mPromise(new typename AllSettledPromiseType::Private(__func__)),
368 mOutstandingPromises(aDependentPromises) {
369 MOZ_ASSERT(aDependentPromises > 0);
370 mValues.SetLength(aDependentPromises);
373 void Settle(size_t aIndex, ResolveOrRejectValueParam aValue) {
374 if (!mPromise) {
375 // Already rejected.
376 return;
379 mValues[aIndex].emplace(MaybeMove(aValue));
380 if (--mOutstandingPromises == 0) {
381 nsTArray<ResolveOrRejectValue> values;
382 values.SetCapacity(mValues.Length());
383 for (auto&& value : mValues) {
384 values.AppendElement(std::move(value.ref()));
387 mPromise->Resolve(std::move(values), __func__);
388 mPromise = nullptr;
389 mValues.Clear();
393 AllSettledPromiseType* Promise() { return mPromise; }
395 private:
396 nsTArray<Maybe<ResolveOrRejectValue>> mValues;
397 RefPtr<typename AllSettledPromiseType::Private> mPromise;
398 size_t mOutstandingPromises;
401 public:
402 [[nodiscard]] static RefPtr<AllPromiseType> All(
403 nsISerialEventTarget* aProcessingTarget,
404 nsTArray<RefPtr<MozPromise>>& aPromises) {
405 if (aPromises.Length() == 0) {
406 return AllPromiseType::CreateAndResolve(
407 CopyableTArray<ResolveValueType>(), __func__);
410 RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
411 RefPtr<AllPromiseType> promise = holder->Promise();
412 for (size_t i = 0; i < aPromises.Length(); ++i) {
413 aPromises[i]->Then(
414 aProcessingTarget, __func__,
415 [holder, i](ResolveValueTypeParam aResolveValue) -> void {
416 holder->Resolve(i, MaybeMove(aResolveValue));
418 [holder](RejectValueTypeParam aRejectValue) -> void {
419 holder->Reject(MaybeMove(aRejectValue));
422 return promise;
425 [[nodiscard]] static RefPtr<AllSettledPromiseType> AllSettled(
426 nsISerialEventTarget* aProcessingTarget,
427 nsTArray<RefPtr<MozPromise>>& aPromises) {
428 if (aPromises.Length() == 0) {
429 return AllSettledPromiseType::CreateAndResolve(
430 CopyableTArray<ResolveOrRejectValue>(), __func__);
433 RefPtr<AllSettledPromiseHolder> holder =
434 new AllSettledPromiseHolder(aPromises.Length());
435 RefPtr<AllSettledPromiseType> promise = holder->Promise();
436 for (size_t i = 0; i < aPromises.Length(); ++i) {
437 aPromises[i]->Then(aProcessingTarget, __func__,
438 [holder, i](ResolveOrRejectValueParam aValue) -> void {
439 holder->Settle(i, MaybeMove(aValue));
442 return promise;
445 class Request : public MozPromiseRefcountable {
446 public:
447 virtual void Disconnect() = 0;
449 protected:
450 Request() : mComplete(false), mDisconnected(false) {}
451 virtual ~Request() = default;
453 bool mComplete;
454 bool mDisconnected;
457 protected:
459 * A ThenValue tracks a single consumer waiting on the promise. When a
460 * consumer invokes promise->Then(...), a ThenValue is created. Once the
461 * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
462 * which invokes the resolve/reject method and then deletes the ThenValue.
464 class ThenValueBase : public Request {
465 friend class MozPromise;
466 static const uint32_t sMagic = 0xfadece11;
468 public:
469 class ResolveOrRejectRunnable : public CancelableRunnable {
470 public:
471 ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
472 : CancelableRunnable(
473 "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
474 mThenValue(aThenValue),
475 mPromise(aPromise) {
476 MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
479 ~ResolveOrRejectRunnable() {
480 if (mThenValue) {
481 mThenValue->AssertIsDead();
485 NS_IMETHOD Run() override {
486 PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
487 mThenValue->DoResolveOrReject(mPromise->Value());
488 mThenValue = nullptr;
489 mPromise = nullptr;
490 return NS_OK;
493 nsresult Cancel() override { return Run(); }
495 private:
496 RefPtr<ThenValueBase> mThenValue;
497 RefPtr<MozPromise> mPromise;
500 ThenValueBase(nsISerialEventTarget* aResponseTarget, const char* aCallSite)
501 : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {
502 MOZ_ASSERT(aResponseTarget);
505 # ifdef PROMISE_DEBUG
506 ~ThenValueBase() {
507 mMagic1 = 0;
508 mMagic2 = 0;
510 # endif
512 void AssertIsDead() {
513 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
514 // We want to assert that this ThenValues is dead - that is to say, that
515 // there are no consumers waiting for the result. In the case of a normal
516 // ThenValue, we check that it has been disconnected, which is the way
517 // that the consumer signals that it no longer wishes to hear about the
518 // result. If this ThenValue has a completion promise (which is mutually
519 // exclusive with being disconnectable), we recursively assert that every
520 // ThenValue associated with the completion promise is dead.
521 if (MozPromiseBase* p = CompletionPromise()) {
522 p->AssertIsDead();
523 } else {
524 MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
528 void Dispatch(MozPromise* aPromise) {
529 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
530 aPromise->mMutex.AssertCurrentThreadOwns();
531 MOZ_ASSERT(!aPromise->IsPending());
533 nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
534 PROMISE_LOG(
535 "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
536 "%s dispatch",
537 aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
538 r.get(), aPromise, this,
539 aPromise->mUseSynchronousTaskDispatch ? "synchronous"
540 : aPromise->mUseDirectTaskDispatch ? "directtask"
541 : "normal");
543 if (aPromise->mUseSynchronousTaskDispatch &&
544 mResponseTarget->IsOnCurrentThread()) {
545 PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
546 this);
547 r->Run();
548 return;
551 if (aPromise->mUseDirectTaskDispatch &&
552 mResponseTarget->IsOnCurrentThread()) {
553 PROMISE_LOG(
554 "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
555 this);
556 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
557 do_QueryInterface(mResponseTarget);
558 if (dispatcher) {
559 dispatcher->DispatchDirectTask(r.forget());
560 return;
562 NS_WARNING(
563 nsPrintfCString(
564 "Direct Task dispatching not available for thread \"%s\"",
565 PR_GetThreadName(PR_GetCurrentThread()))
566 .get());
567 MOZ_DIAGNOSTIC_ASSERT(
568 false,
569 "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
570 "task dispatching");
573 // Promise consumers are allowed to disconnect the Request object and
574 // then shut down the thread or task queue that the promise result would
575 // be dispatched on. So we unfortunately can't assert that promise
576 // dispatch succeeds. :-(
577 mResponseTarget->Dispatch(r.forget());
580 void Disconnect() override {
581 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
582 MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
583 Request::mDisconnected = true;
585 // We could support rejecting the completion promise on disconnection, but
586 // then we'd need to have some sort of default reject value. The use cases
587 // of disconnection and completion promise chaining seem pretty
588 // orthogonal, so let's use assert against it.
589 MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
592 protected:
593 virtual MozPromiseBase* CompletionPromise() const = 0;
594 virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
596 void DoResolveOrReject(ResolveOrRejectValue& aValue) {
597 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
598 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
599 Request::mComplete = true;
600 if (Request::mDisconnected) {
601 PROMISE_LOG(
602 "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
603 this);
604 return;
607 // Invoke the resolve or reject method.
608 DoResolveOrRejectInternal(aValue);
611 nsCOMPtr<nsISerialEventTarget>
612 mResponseTarget; // May be released on any thread.
613 # ifdef PROMISE_DEBUG
614 uint32_t mMagic1 = sMagic;
615 # endif
616 const char* mCallSite;
617 # ifdef PROMISE_DEBUG
618 uint32_t mMagic2 = sMagic;
619 # endif
623 * We create two overloads for invoking Resolve/Reject Methods so as to
624 * make the resolve/reject value argument "optional".
626 template <typename ThisType, typename MethodType, typename ValueType>
627 static std::enable_if_t<TakesArgument<MethodType>::value,
628 typename detail::MethodTrait<MethodType>::ReturnType>
629 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
630 return (aThisVal->*aMethod)(std::forward<ValueType>(aValue));
633 template <typename ThisType, typename MethodType, typename ValueType>
634 static std::enable_if_t<!TakesArgument<MethodType>::value,
635 typename detail::MethodTrait<MethodType>::ReturnType>
636 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
637 return (aThisVal->*aMethod)();
640 // Called when promise chaining is supported.
641 template <bool SupportChaining, typename ThisType, typename MethodType,
642 typename ValueType, typename CompletionPromiseType>
643 static std::enable_if_t<SupportChaining, void> InvokeCallbackMethod(
644 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
645 CompletionPromiseType&& aCompletionPromise) {
646 auto p = InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
647 if (aCompletionPromise) {
648 p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
652 // Called when promise chaining is not supported.
653 template <bool SupportChaining, typename ThisType, typename MethodType,
654 typename ValueType, typename CompletionPromiseType>
655 static std::enable_if_t<!SupportChaining, void> InvokeCallbackMethod(
656 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
657 CompletionPromiseType&& aCompletionPromise) {
658 MOZ_DIAGNOSTIC_ASSERT(
659 !aCompletionPromise,
660 "Can't do promise chaining for a non-promise-returning method.");
661 InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
664 template <typename>
665 class ThenCommand;
667 template <typename...>
668 class ThenValue;
670 template <typename ThisType, typename ResolveMethodType,
671 typename RejectMethodType>
672 class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
673 : public ThenValueBase {
674 friend class ThenCommand<ThenValue>;
676 using R1 = typename RemoveSmartPointer<
677 typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
678 using R2 = typename RemoveSmartPointer<
679 typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
680 using SupportChaining =
681 std::integral_constant<bool, IsMozPromise<R1>::value &&
682 std::is_same_v<R1, R2>>;
684 // Fall back to MozPromise when promise chaining is not supported to make
685 // code compile.
686 using PromiseType =
687 std::conditional_t<SupportChaining::value, R1, MozPromise>;
689 public:
690 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
691 ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
692 const char* aCallSite)
693 : ThenValueBase(aResponseTarget, aCallSite),
694 mThisVal(aThisVal),
695 mResolveMethod(aResolveMethod),
696 mRejectMethod(aRejectMethod) {}
698 void Disconnect() override {
699 ThenValueBase::Disconnect();
701 // If a Request has been disconnected, we don't guarantee that the
702 // resolve/reject runnable will be dispatched. Null out our refcounted
703 // this-value now so that it's released predictably on the dispatch
704 // thread.
705 mThisVal = nullptr;
708 protected:
709 MozPromiseBase* CompletionPromise() const override {
710 return mCompletionPromise;
713 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
714 if (aValue.IsResolve()) {
715 InvokeCallbackMethod<SupportChaining::value>(
716 mThisVal.get(), mResolveMethod, MaybeMove(aValue.ResolveValue()),
717 std::move(mCompletionPromise));
718 } else {
719 InvokeCallbackMethod<SupportChaining::value>(
720 mThisVal.get(), mRejectMethod, MaybeMove(aValue.RejectValue()),
721 std::move(mCompletionPromise));
724 // Null out mThisVal after invoking the callback so that any references
725 // are released predictably on the dispatch thread. Otherwise, it would be
726 // released on whatever thread last drops its reference to the ThenValue,
727 // which may or may not be ok.
728 mThisVal = nullptr;
731 private:
732 RefPtr<ThisType>
733 mThisVal; // Only accessed and refcounted on dispatch thread.
734 ResolveMethodType mResolveMethod;
735 RejectMethodType mRejectMethod;
736 RefPtr<typename PromiseType::Private> mCompletionPromise;
739 template <typename ThisType, typename ResolveRejectMethodType>
740 class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase {
741 friend class ThenCommand<ThenValue>;
743 using R1 = typename RemoveSmartPointer<typename detail::MethodTrait<
744 ResolveRejectMethodType>::ReturnType>::Type;
745 using SupportChaining =
746 std::integral_constant<bool, IsMozPromise<R1>::value>;
748 // Fall back to MozPromise when promise chaining is not supported to make
749 // code compile.
750 using PromiseType =
751 std::conditional_t<SupportChaining::value, R1, MozPromise>;
753 public:
754 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
755 ResolveRejectMethodType aResolveRejectMethod,
756 const char* aCallSite)
757 : ThenValueBase(aResponseTarget, aCallSite),
758 mThisVal(aThisVal),
759 mResolveRejectMethod(aResolveRejectMethod) {}
761 void Disconnect() override {
762 ThenValueBase::Disconnect();
764 // If a Request has been disconnected, we don't guarantee that the
765 // resolve/reject runnable will be dispatched. Null out our refcounted
766 // this-value now so that it's released predictably on the dispatch
767 // thread.
768 mThisVal = nullptr;
771 protected:
772 MozPromiseBase* CompletionPromise() const override {
773 return mCompletionPromise;
776 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
777 InvokeCallbackMethod<SupportChaining::value>(
778 mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue),
779 std::move(mCompletionPromise));
781 // Null out mThisVal after invoking the callback so that any references
782 // are released predictably on the dispatch thread. Otherwise, it would be
783 // released on whatever thread last drops its reference to the ThenValue,
784 // which may or may not be ok.
785 mThisVal = nullptr;
788 private:
789 RefPtr<ThisType>
790 mThisVal; // Only accessed and refcounted on dispatch thread.
791 ResolveRejectMethodType mResolveRejectMethod;
792 RefPtr<typename PromiseType::Private> mCompletionPromise;
795 // NB: We could use std::function here instead of a template if it were
796 // supported. :-(
797 template <typename ResolveFunction, typename RejectFunction>
798 class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase {
799 friend class ThenCommand<ThenValue>;
801 using R1 = typename RemoveSmartPointer<
802 typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
803 using R2 = typename RemoveSmartPointer<
804 typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
805 using SupportChaining =
806 std::integral_constant<bool, IsMozPromise<R1>::value &&
807 std::is_same_v<R1, R2>>;
809 // Fall back to MozPromise when promise chaining is not supported to make
810 // code compile.
811 using PromiseType =
812 std::conditional_t<SupportChaining::value, R1, MozPromise>;
814 public:
815 ThenValue(nsISerialEventTarget* aResponseTarget,
816 ResolveFunction&& aResolveFunction,
817 RejectFunction&& aRejectFunction, const char* aCallSite)
818 : ThenValueBase(aResponseTarget, aCallSite) {
819 mResolveFunction.emplace(std::move(aResolveFunction));
820 mRejectFunction.emplace(std::move(aRejectFunction));
823 void Disconnect() override {
824 ThenValueBase::Disconnect();
826 // If a Request has been disconnected, we don't guarantee that the
827 // resolve/reject runnable will be dispatched. Destroy our callbacks
828 // now so that any references in closures are released predictable on
829 // the dispatch thread.
830 mResolveFunction.reset();
831 mRejectFunction.reset();
834 protected:
835 MozPromiseBase* CompletionPromise() const override {
836 return mCompletionPromise;
839 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
840 // Note: The usage of InvokeCallbackMethod here requires that
841 // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
842 // classes with ::operator()), since it allows us to share code more
843 // easily. We could fix this if need be, though it's quite easy to work
844 // around by just capturing something.
845 if (aValue.IsResolve()) {
846 InvokeCallbackMethod<SupportChaining::value>(
847 mResolveFunction.ptr(), &ResolveFunction::operator(),
848 MaybeMove(aValue.ResolveValue()), std::move(mCompletionPromise));
849 } else {
850 InvokeCallbackMethod<SupportChaining::value>(
851 mRejectFunction.ptr(), &RejectFunction::operator(),
852 MaybeMove(aValue.RejectValue()), std::move(mCompletionPromise));
855 // Destroy callbacks after invocation so that any references in closures
856 // are released predictably on the dispatch thread. Otherwise, they would
857 // be released on whatever thread last drops its reference to the
858 // ThenValue, which may or may not be ok.
859 mResolveFunction.reset();
860 mRejectFunction.reset();
863 private:
864 Maybe<ResolveFunction>
865 mResolveFunction; // Only accessed and deleted on dispatch thread.
866 Maybe<RejectFunction>
867 mRejectFunction; // Only accessed and deleted on dispatch thread.
868 RefPtr<typename PromiseType::Private> mCompletionPromise;
871 template <typename ResolveRejectFunction>
872 class ThenValue<ResolveRejectFunction> : public ThenValueBase {
873 friend class ThenCommand<ThenValue>;
875 using R1 = typename RemoveSmartPointer<
876 typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
877 using SupportChaining =
878 std::integral_constant<bool, IsMozPromise<R1>::value>;
880 // Fall back to MozPromise when promise chaining is not supported to make
881 // code compile.
882 using PromiseType =
883 std::conditional_t<SupportChaining::value, R1, MozPromise>;
885 public:
886 ThenValue(nsISerialEventTarget* aResponseTarget,
887 ResolveRejectFunction&& aResolveRejectFunction,
888 const char* aCallSite)
889 : ThenValueBase(aResponseTarget, aCallSite) {
890 mResolveRejectFunction.emplace(std::move(aResolveRejectFunction));
893 void Disconnect() override {
894 ThenValueBase::Disconnect();
896 // If a Request has been disconnected, we don't guarantee that the
897 // resolve/reject runnable will be dispatched. Destroy our callbacks
898 // now so that any references in closures are released predictable on
899 // the dispatch thread.
900 mResolveRejectFunction.reset();
903 protected:
904 MozPromiseBase* CompletionPromise() const override {
905 return mCompletionPromise;
908 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
909 // Note: The usage of InvokeCallbackMethod here requires that
910 // ResolveRejectFunction is capture-lambdas (i.e. anonymous
911 // classes with ::operator()), since it allows us to share code more
912 // easily. We could fix this if need be, though it's quite easy to work
913 // around by just capturing something.
914 InvokeCallbackMethod<SupportChaining::value>(
915 mResolveRejectFunction.ptr(), &ResolveRejectFunction::operator(),
916 MaybeMove(aValue), std::move(mCompletionPromise));
918 // Destroy callbacks after invocation so that any references in closures
919 // are released predictably on the dispatch thread. Otherwise, they would
920 // be released on whatever thread last drops its reference to the
921 // ThenValue, which may or may not be ok.
922 mResolveRejectFunction.reset();
925 private:
926 Maybe<ResolveRejectFunction>
927 mResolveRejectFunction; // Only accessed and deleted on dispatch
928 // thread.
929 RefPtr<typename PromiseType::Private> mCompletionPromise;
932 public:
933 void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
934 const char* aCallSite) {
935 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
936 mMagic3 == sMagic && mMagic4 == &mMutex);
937 RefPtr<ThenValueBase> thenValue = aThenValue;
938 MutexAutoLock lock(mMutex);
939 MOZ_DIAGNOSTIC_ASSERT(
940 !IsExclusive || !mHaveRequest,
941 "Using an exclusive promise in a non-exclusive fashion");
942 mHaveRequest = true;
943 PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
944 aCallSite, this, thenValue.get(), (int)IsPending());
945 if (!IsPending()) {
946 thenValue->Dispatch(this);
947 } else {
948 mThenValues.AppendElement(thenValue.forget());
952 protected:
954 * A command object to store all information needed to make a request to
955 * the promise. This allows us to delay the request until further use is
956 * known (whether it is ->Then() again for more promise chaining or ->Track()
957 * to terminate chaining and issue the request).
959 * This allows a unified syntax for promise chaining and disconnection
960 * and feels more like its JS counterpart.
962 template <typename ThenValueType>
963 class ThenCommand {
964 // Allow Promise1::ThenCommand to access the private constructor,
965 // Promise2::ThenCommand(ThenCommand&&).
966 template <typename, typename, bool>
967 friend class MozPromise;
969 using PromiseType = typename ThenValueType::PromiseType;
970 using Private = typename PromiseType::Private;
972 ThenCommand(const char* aCallSite,
973 already_AddRefed<ThenValueType> aThenValue,
974 MozPromise* aReceiver)
975 : mCallSite(aCallSite), mThenValue(aThenValue), mReceiver(aReceiver) {}
977 ThenCommand(ThenCommand&& aOther) = default;
979 public:
980 ~ThenCommand() {
981 // Issue the request now if the return value of Then() is not used.
982 if (mThenValue) {
983 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
987 // Allow RefPtr<MozPromise> p = somePromise->Then();
988 // p->Then(thread1, ...);
989 // p->Then(thread2, ...);
990 operator RefPtr<PromiseType>() {
991 static_assert(
992 ThenValueType::SupportChaining::value,
993 "The resolve/reject callback needs to return a RefPtr<MozPromise> "
994 "in order to do promise chaining.");
996 // mCompletionPromise must be created before ThenInternal() to avoid race.
997 RefPtr<Private> p =
998 new Private("<completion promise>", true /* aIsCompletionPromise */);
999 mThenValue->mCompletionPromise = p;
1000 // Note ThenInternal() might nullify mCompletionPromise before return.
1001 // So we need to return p instead of mCompletionPromise.
1002 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
1003 return p;
1006 template <typename... Ts>
1007 auto Then(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().Then(
1008 std::forward<Ts>(aArgs)...)) {
1009 return static_cast<RefPtr<PromiseType>>(*this)->Then(
1010 std::forward<Ts>(aArgs)...);
1013 void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder) {
1014 aRequestHolder.Track(do_AddRef(mThenValue));
1015 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
1018 // Allow calling ->Then() again for more promise chaining or ->Track() to
1019 // end chaining and track the request for future disconnection.
1020 ThenCommand* operator->() { return this; }
1022 private:
1023 const char* mCallSite;
1024 RefPtr<ThenValueType> mThenValue;
1025 RefPtr<MozPromise> mReceiver;
1028 public:
1029 template <typename ThisType, typename... Methods,
1030 typename ThenValueType = ThenValue<ThisType*, Methods...>,
1031 typename ReturnType = ThenCommand<ThenValueType>>
1032 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
1033 ThisType* aThisVal, Methods... aMethods) {
1034 RefPtr<ThenValueType> thenValue =
1035 new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
1036 return ReturnType(aCallSite, thenValue.forget(), this);
1039 template <typename... Functions,
1040 typename ThenValueType = ThenValue<Functions...>,
1041 typename ReturnType = ThenCommand<ThenValueType>>
1042 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
1043 Functions&&... aFunctions) {
1044 RefPtr<ThenValueType> thenValue =
1045 new ThenValueType(aResponseTarget, std::move(aFunctions)..., aCallSite);
1046 return ReturnType(aCallSite, thenValue.forget(), this);
1049 void ChainTo(already_AddRefed<Private> aChainedPromise,
1050 const char* aCallSite) {
1051 MutexAutoLock lock(mMutex);
1052 MOZ_DIAGNOSTIC_ASSERT(
1053 !IsExclusive || !mHaveRequest,
1054 "Using an exclusive promise in a non-exclusive fashion");
1055 mHaveRequest = true;
1056 RefPtr<Private> chainedPromise = aChainedPromise;
1057 PROMISE_LOG(
1058 "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
1059 aCallSite, this, chainedPromise.get(), (int)IsPending());
1061 // We want to use the same type of dispatching method with the chained
1062 // promises.
1064 // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
1065 // at compilation time to ensure we're not triggering the static_assert in
1066 // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
1067 // that.
1068 if (mUseDirectTaskDispatch) {
1069 chainedPromise->UseDirectTaskDispatch(aCallSite);
1070 } else if constexpr (IsExclusive) {
1071 if (mUseSynchronousTaskDispatch) {
1072 chainedPromise->UseSynchronousTaskDispatch(aCallSite);
1076 if (!IsPending()) {
1077 ForwardTo(chainedPromise);
1078 } else {
1079 mChainedPromises.AppendElement(chainedPromise);
1083 # ifdef MOZ_WIDGET_ANDROID
1084 // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
1085 [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult(
1086 java::GeckoResult::Param aGeckoResult) {
1087 using jni::GeckoResultCallback;
1088 RefPtr<Private> p = new Private("GeckoResult Glue", false);
1089 auto resolve = GeckoResultCallback::CreateAndAttach<ResolveValueType>(
1090 [p](ResolveValueType&& aArg) {
1091 p->Resolve(MaybeMove(aArg), __func__);
1093 auto reject = GeckoResultCallback::CreateAndAttach<RejectValueType>(
1094 [p](RejectValueType&& aArg) { p->Reject(MaybeMove(aArg), __func__); });
1095 aGeckoResult->NativeThen(resolve, reject);
1096 return p;
1098 # endif
1100 // Creates a C++ MozPromise from its JS counterpart, dom::Promise.
1101 // FromDomPromise currently only supports primitive types (int8/16/32, float,
1102 // double) And the reject value type must be a nsresult.
1103 // To use, please include MozPromiseInlines.h
1104 static RefPtr<MozPromise> FromDomPromise(dom::Promise* aDOMPromise);
1106 // Note we expose the function AssertIsDead() instead of IsDead() since
1107 // checking IsDead() is a data race in the situation where the request is not
1108 // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1109 // AssertIsDead() only.
1110 void AssertIsDead() override {
1111 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1112 mMagic3 == sMagic && mMagic4 == &mMutex);
1113 MutexAutoLock lock(mMutex);
1114 for (auto&& then : mThenValues) {
1115 then->AssertIsDead();
1117 for (auto&& chained : mChainedPromises) {
1118 chained->AssertIsDead();
1122 bool IsResolved() const { return mValue.IsResolve(); }
1124 protected:
1125 bool IsPending() const { return mValue.IsNothing(); }
1127 ResolveOrRejectValue& Value() {
1128 // This method should only be called once the value has stabilized. As
1129 // such, we don't need to acquire the lock here.
1130 MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1131 return mValue;
1134 void DispatchAll() {
1135 mMutex.AssertCurrentThreadOwns();
1136 for (auto&& thenValue : mThenValues) {
1137 thenValue->Dispatch(this);
1139 mThenValues.Clear();
1141 for (auto&& chainedPromise : mChainedPromises) {
1142 ForwardTo(chainedPromise);
1144 mChainedPromises.Clear();
1147 void ForwardTo(Private* aOther) {
1148 MOZ_ASSERT(!IsPending());
1149 if (mValue.IsResolve()) {
1150 aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
1151 } else {
1152 aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
1156 virtual ~MozPromise() {
1157 PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1158 AssertIsDead();
1159 // We can't guarantee a completion promise will always be revolved or
1160 // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1161 if (!mIsCompletionPromise) {
1162 MOZ_ASSERT(!IsPending());
1163 MOZ_ASSERT(mThenValues.IsEmpty());
1164 MOZ_ASSERT(mChainedPromises.IsEmpty());
1166 # ifdef PROMISE_DEBUG
1167 mMagic1 = 0;
1168 mMagic2 = 0;
1169 mMagic3 = 0;
1170 mMagic4 = nullptr;
1171 # endif
1174 const char* mCreationSite; // For logging
1175 Mutex mMutex;
1176 ResolveOrRejectValue mValue;
1177 bool mUseSynchronousTaskDispatch = false;
1178 bool mUseDirectTaskDispatch = false;
1179 # ifdef PROMISE_DEBUG
1180 uint32_t mMagic1 = sMagic;
1181 # endif
1182 // Try shows we never have more than 3 elements when IsExclusive is false.
1183 // So '3' is a good value to avoid heap allocation in most cases.
1184 AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
1185 # ifdef PROMISE_DEBUG
1186 uint32_t mMagic2 = sMagic;
1187 # endif
1188 nsTArray<RefPtr<Private>> mChainedPromises;
1189 # ifdef PROMISE_DEBUG
1190 uint32_t mMagic3 = sMagic;
1191 # endif
1192 bool mHaveRequest;
1193 const bool mIsCompletionPromise;
1194 # ifdef PROMISE_DEBUG
1195 void* mMagic4;
1196 # endif
1199 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
1200 class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
1201 : public MozPromise<ResolveValueT, RejectValueT, IsExclusive> {
1202 public:
1203 explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
1204 : MozPromise(aCreationSite, aIsCompletionPromise) {}
1206 template <typename ResolveValueT_>
1207 void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite) {
1208 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1209 mMagic3 == sMagic && mMagic4 == &mMutex);
1210 MutexAutoLock lock(mMutex);
1211 PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite,
1212 this, mCreationSite);
1213 if (!IsPending()) {
1214 PROMISE_LOG(
1215 "%s ignored already resolved or rejected MozPromise (%p created at "
1216 "%s)",
1217 aResolveSite, this, mCreationSite);
1218 return;
1220 mValue.SetResolve(std::forward<ResolveValueT_>(aResolveValue));
1221 DispatchAll();
1224 template <typename RejectValueT_>
1225 void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite) {
1226 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1227 mMagic3 == sMagic && mMagic4 == &mMutex);
1228 MutexAutoLock lock(mMutex);
1229 PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this,
1230 mCreationSite);
1231 if (!IsPending()) {
1232 PROMISE_LOG(
1233 "%s ignored already resolved or rejected MozPromise (%p created at "
1234 "%s)",
1235 aRejectSite, this, mCreationSite);
1236 return;
1238 mValue.SetReject(std::forward<RejectValueT_>(aRejectValue));
1239 DispatchAll();
1242 template <typename ResolveOrRejectValue_>
1243 void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite) {
1244 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1245 mMagic3 == sMagic && mMagic4 == &mMutex);
1246 MutexAutoLock lock(mMutex);
1247 PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite,
1248 this, mCreationSite);
1249 if (!IsPending()) {
1250 PROMISE_LOG(
1251 "%s ignored already resolved or rejected MozPromise (%p created at "
1252 "%s)",
1253 aSite, this, mCreationSite);
1254 return;
1256 mValue = std::forward<ResolveOrRejectValue_>(aValue);
1257 DispatchAll();
1260 // If the caller and target are both on the same thread, run the the resolve
1261 // or reject callback synchronously. Otherwise, the task will be dispatched
1262 // via the target Dispatch method.
1263 void UseSynchronousTaskDispatch(const char* aSite) {
1264 static_assert(
1265 IsExclusive,
1266 "Synchronous dispatch can only be used with exclusive promises");
1267 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1268 mMagic3 == sMagic && mMagic4 == &mMutex);
1269 MutexAutoLock lock(mMutex);
1270 PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
1271 aSite, this, mCreationSite);
1272 MOZ_ASSERT(IsPending(),
1273 "A Promise must not have been already resolved or rejected to "
1274 "set dispatch state");
1275 mUseSynchronousTaskDispatch = true;
1278 // If the caller and target are both on the same thread, run the
1279 // resolve/reject callback off the direct task queue instead. This avoids a
1280 // full trip to the back of the event queue for each additional asynchronous
1281 // step when using MozPromise, and is similar (but not identical to) the
1282 // microtask semantics of JS promises.
1283 void UseDirectTaskDispatch(const char* aSite) {
1284 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1285 mMagic3 == sMagic && mMagic4 == &mMutex);
1286 MutexAutoLock lock(mMutex);
1287 PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite,
1288 this, mCreationSite);
1289 MOZ_ASSERT(IsPending(),
1290 "A Promise must not have been already resolved or rejected to "
1291 "set dispatch state");
1292 MOZ_ASSERT(!mUseSynchronousTaskDispatch,
1293 "Promise already set for synchronous dispatch");
1294 mUseDirectTaskDispatch = true;
1298 // A generic promise type that does the trick for simple use cases.
1299 typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> GenericPromise;
1301 // A generic, non-exclusive promise type that does the trick for simple use
1302 // cases.
1303 typedef MozPromise<bool, nsresult, /* IsExclusive = */ false>
1304 GenericNonExclusivePromise;
1307 * Class to encapsulate a promise for a particular role. Use this as the member
1308 * variable for a class whose method returns a promise.
1310 template <typename PromiseType, typename ImplType>
1311 class MozPromiseHolderBase {
1312 public:
1313 MozPromiseHolderBase() = default;
1315 MozPromiseHolderBase(MozPromiseHolderBase&& aOther) = default;
1316 MozPromiseHolderBase& operator=(MozPromiseHolderBase&& aOther) = default;
1318 ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise); }
1320 already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
1321 static_cast<ImplType*>(this)->Check();
1322 if (!mPromise) {
1323 mPromise = new (typename PromiseType::Private)(aMethodName);
1325 RefPtr<PromiseType> p = mPromise.get();
1326 return p.forget();
1329 bool IsEmpty() const {
1330 static_cast<const ImplType*>(this)->Check();
1331 return !mPromise;
1334 already_AddRefed<typename PromiseType::Private> Steal() {
1335 static_cast<ImplType*>(this)->Check();
1336 return mPromise.forget();
1339 template <typename ResolveValueType_>
1340 void Resolve(ResolveValueType_&& aResolveValue, const char* aMethodName) {
1341 static_assert(std::is_convertible_v<ResolveValueType_,
1342 typename PromiseType::ResolveValueType>,
1343 "Resolve() argument must be implicitly convertible to "
1344 "MozPromise's ResolveValueT");
1346 static_cast<ImplType*>(this)->Check();
1347 MOZ_ASSERT(mPromise);
1348 mPromise->Resolve(std::forward<ResolveValueType_>(aResolveValue),
1349 aMethodName);
1350 mPromise = nullptr;
1353 template <typename ResolveValueType_>
1354 void ResolveIfExists(ResolveValueType_&& aResolveValue,
1355 const char* aMethodName) {
1356 if (!IsEmpty()) {
1357 Resolve(std::forward<ResolveValueType_>(aResolveValue), aMethodName);
1361 template <typename RejectValueType_>
1362 void Reject(RejectValueType_&& aRejectValue, const char* aMethodName) {
1363 static_assert(std::is_convertible_v<RejectValueType_,
1364 typename PromiseType::RejectValueType>,
1365 "Reject() argument must be implicitly convertible to "
1366 "MozPromise's RejectValueT");
1368 static_cast<ImplType*>(this)->Check();
1369 MOZ_ASSERT(mPromise);
1370 mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1371 mPromise = nullptr;
1374 template <typename RejectValueType_>
1375 void RejectIfExists(RejectValueType_&& aRejectValue,
1376 const char* aMethodName) {
1377 if (!IsEmpty()) {
1378 Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1382 template <typename ResolveOrRejectValueType_>
1383 void ResolveOrReject(ResolveOrRejectValueType_&& aValue,
1384 const char* aMethodName) {
1385 static_cast<ImplType*>(this)->Check();
1386 MOZ_ASSERT(mPromise);
1387 mPromise->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1388 aMethodName);
1389 mPromise = nullptr;
1392 template <typename ResolveOrRejectValueType_>
1393 void ResolveOrRejectIfExists(ResolveOrRejectValueType_&& aValue,
1394 const char* aMethodName) {
1395 if (!IsEmpty()) {
1396 ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1397 aMethodName);
1401 void UseSynchronousTaskDispatch(const char* aSite) {
1402 MOZ_ASSERT(mPromise);
1403 mPromise->UseSynchronousTaskDispatch(aSite);
1406 void UseDirectTaskDispatch(const char* aSite) {
1407 MOZ_ASSERT(mPromise);
1408 mPromise->UseDirectTaskDispatch(aSite);
1411 private:
1412 RefPtr<typename PromiseType::Private> mPromise;
1415 template <typename PromiseType>
1416 class MozPromiseHolder
1417 : public MozPromiseHolderBase<PromiseType, MozPromiseHolder<PromiseType>> {
1418 public:
1419 using MozPromiseHolderBase<
1420 PromiseType, MozPromiseHolder<PromiseType>>::MozPromiseHolderBase;
1421 static constexpr void Check(){};
1424 template <typename PromiseType>
1425 class MozMonitoredPromiseHolder
1426 : public MozPromiseHolderBase<PromiseType,
1427 MozMonitoredPromiseHolder<PromiseType>> {
1428 public:
1429 // Provide a Monitor that should always be held when accessing this instance.
1430 explicit MozMonitoredPromiseHolder(Monitor* const aMonitor)
1431 : mMonitor(aMonitor) {
1432 MOZ_ASSERT(aMonitor);
1435 MozMonitoredPromiseHolder(MozMonitoredPromiseHolder&& aOther) = delete;
1436 MozMonitoredPromiseHolder& operator=(MozMonitoredPromiseHolder&& aOther) =
1437 delete;
1439 void Check() const { mMonitor->AssertCurrentThreadOwns(); }
1441 private:
1442 Monitor* const mMonitor;
1446 * Class to encapsulate a MozPromise::Request reference. Use this as the member
1447 * variable for a class waiting on a MozPromise.
1449 template <typename PromiseType>
1450 class MozPromiseRequestHolder {
1451 public:
1452 MozPromiseRequestHolder() = default;
1453 ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
1455 void Track(already_AddRefed<typename PromiseType::Request> aRequest) {
1456 MOZ_DIAGNOSTIC_ASSERT(!Exists());
1457 mRequest = aRequest;
1460 void Complete() {
1461 MOZ_DIAGNOSTIC_ASSERT(Exists());
1462 mRequest = nullptr;
1465 // Disconnects and forgets an outstanding promise. The resolve/reject methods
1466 // will never be called.
1467 void Disconnect() {
1468 MOZ_ASSERT(Exists());
1469 mRequest->Disconnect();
1470 mRequest = nullptr;
1473 void DisconnectIfExists() {
1474 if (Exists()) {
1475 Disconnect();
1479 bool Exists() const { return !!mRequest; }
1481 private:
1482 RefPtr<typename PromiseType::Request> mRequest;
1485 // Asynchronous Potentially-Cross-Thread Method Calls.
1487 // This machinery allows callers to schedule a promise-returning function
1488 // (a method and object, or a function object like a lambda) to be invoked
1489 // asynchronously on a given thread, while at the same time receiving a
1490 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1491 // task to invoke the function on the proper thread and also chain the
1492 // resulting promise to the one that the caller received, so that resolve/
1493 // reject values are forwarded through.
1495 namespace detail {
1497 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1498 // assertions when used on templated types.
1499 class MethodCallBase {
1500 public:
1501 MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase)
1502 MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase)
1505 template <typename PromiseType, typename MethodType, typename ThisType,
1506 typename... Storages>
1507 class MethodCall : public MethodCallBase {
1508 public:
1509 template <typename... Args>
1510 MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
1511 : mMethod(aMethod),
1512 mThisVal(aThisVal),
1513 mArgs(std::forward<Args>(aArgs)...) {
1514 static_assert(sizeof...(Storages) == sizeof...(Args),
1515 "Storages and Args should have equal sizes");
1518 RefPtr<PromiseType> Invoke() { return mArgs.apply(mThisVal.get(), mMethod); }
1520 private:
1521 MethodType mMethod;
1522 RefPtr<ThisType> mThisVal;
1523 RunnableMethodArguments<Storages...> mArgs;
1526 template <typename PromiseType, typename MethodType, typename ThisType,
1527 typename... Storages>
1528 class ProxyRunnable : public CancelableRunnable {
1529 public:
1530 ProxyRunnable(
1531 typename PromiseType::Private* aProxyPromise,
1532 MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
1533 : CancelableRunnable("detail::ProxyRunnable"),
1534 mProxyPromise(aProxyPromise),
1535 mMethodCall(aMethodCall) {}
1537 NS_IMETHOD Run() override {
1538 RefPtr<PromiseType> p = mMethodCall->Invoke();
1539 mMethodCall = nullptr;
1540 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1541 return NS_OK;
1544 nsresult Cancel() override { return Run(); }
1546 private:
1547 RefPtr<typename PromiseType::Private> mProxyPromise;
1548 UniquePtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>>
1549 mMethodCall;
1552 template <typename... Storages, typename PromiseType, typename ThisType,
1553 typename... ArgTypes, typename... ActualArgTypes>
1554 static RefPtr<PromiseType> InvokeAsyncImpl(
1555 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1556 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1557 ActualArgTypes&&... aArgs) {
1558 MOZ_ASSERT(aTarget);
1560 typedef RefPtr<PromiseType> (ThisType::*MethodType)(ArgTypes...);
1561 typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...>
1562 MethodCallType;
1563 typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...>
1564 ProxyRunnableType;
1566 MethodCallType* methodCall = new MethodCallType(
1567 aMethod, aThisVal, std::forward<ActualArgTypes>(aArgs)...);
1568 RefPtr<typename PromiseType::Private> p =
1569 new (typename PromiseType::Private)(aCallerName);
1570 RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
1571 aTarget->Dispatch(r.forget());
1572 return p;
1575 constexpr bool Any() { return false; }
1577 template <typename T1>
1578 constexpr bool Any(T1 a) {
1579 return static_cast<bool>(a);
1582 template <typename T1, typename... Ts>
1583 constexpr bool Any(T1 a, Ts... aOthers) {
1584 return a || Any(aOthers...);
1587 } // namespace detail
1589 // InvokeAsync with explicitly-specified storages.
1590 // See ParameterStorage in nsThreadUtils.h for help.
1591 template <typename... Storages, typename PromiseType, typename ThisType,
1592 typename... ArgTypes, typename... ActualArgTypes,
1593 std::enable_if_t<sizeof...(Storages) != 0, int> = 0>
1594 static RefPtr<PromiseType> InvokeAsync(
1595 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1596 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1597 ActualArgTypes&&... aArgs) {
1598 static_assert(
1599 sizeof...(Storages) == sizeof...(ArgTypes),
1600 "Provided Storages and method's ArgTypes should have equal sizes");
1601 static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
1602 "Provided Storages and ActualArgTypes should have equal sizes");
1603 return detail::InvokeAsyncImpl<Storages...>(
1604 aTarget, aThisVal, aCallerName, aMethod,
1605 std::forward<ActualArgTypes>(aArgs)...);
1608 // InvokeAsync with no explicitly-specified storages, will copy arguments and
1609 // then move them out of the runnable into the target method parameters.
1610 template <typename... Storages, typename PromiseType, typename ThisType,
1611 typename... ArgTypes, typename... ActualArgTypes,
1612 std::enable_if_t<sizeof...(Storages) == 0, int> = 0>
1613 static RefPtr<PromiseType> InvokeAsync(
1614 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1615 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1616 ActualArgTypes&&... aArgs) {
1617 static_assert(
1618 !detail::Any(
1619 std::is_pointer_v<std::remove_reference_t<ActualArgTypes>>...),
1620 "Cannot pass pointer types through InvokeAsync, Storages must be "
1621 "provided");
1622 static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
1623 "Method's ArgTypes and ActualArgTypes should have equal sizes");
1624 return detail::InvokeAsyncImpl<
1625 StoreCopyPassByRRef<std::decay_t<ActualArgTypes>>...>(
1626 aTarget, aThisVal, aCallerName, aMethod,
1627 std::forward<ActualArgTypes>(aArgs)...);
1630 namespace detail {
1632 template <typename Function, typename PromiseType>
1633 class ProxyFunctionRunnable : public CancelableRunnable {
1634 using FunctionStorage = std::decay_t<Function>;
1636 public:
1637 template <typename F>
1638 ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
1639 F&& aFunction)
1640 : CancelableRunnable("detail::ProxyFunctionRunnable"),
1641 mProxyPromise(aProxyPromise),
1642 mFunction(new FunctionStorage(std::forward<F>(aFunction))) {}
1644 NS_IMETHOD Run() override {
1645 RefPtr<PromiseType> p = (*mFunction)();
1646 mFunction = nullptr;
1647 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1648 return NS_OK;
1651 nsresult Cancel() override { return Run(); }
1653 private:
1654 RefPtr<typename PromiseType::Private> mProxyPromise;
1655 UniquePtr<FunctionStorage> mFunction;
1658 // Note: The following struct and function are not for public consumption (yet?)
1659 // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1660 // function objects). They could be moved outside of detail if really needed.
1662 // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1663 // and their captures). This struct is a tag that allows the use of objects
1664 // through lvalue-refs where necessary.
1665 struct AllowInvokeAsyncFunctionLVRef {};
1667 // Invoke a function object (e.g., lambda or std/mozilla::function)
1668 // asynchronously; note that the object will be copied if provided by
1669 // lvalue-ref. Return a promise that the function should eventually resolve or
1670 // reject.
1671 template <typename Function>
1672 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1673 AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
1674 -> decltype(aFunction()) {
1675 static_assert(
1676 IsRefcountedSmartPointer<decltype(aFunction())>::value &&
1677 IsMozPromise<
1678 typename RemoveSmartPointer<decltype(aFunction())>::Type>::value,
1679 "Function object must return RefPtr<MozPromise>");
1680 MOZ_ASSERT(aTarget);
1681 typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
1682 typedef detail::ProxyFunctionRunnable<Function, PromiseType>
1683 ProxyRunnableType;
1685 auto p = MakeRefPtr<typename PromiseType::Private>(aCallerName);
1686 auto r = MakeRefPtr<ProxyRunnableType>(p, std::forward<Function>(aFunction));
1687 aTarget->Dispatch(r.forget());
1688 return p;
1691 } // namespace detail
1693 // Invoke a function object (e.g., lambda) asynchronously.
1694 // Return a promise that the function should eventually resolve or reject.
1695 template <typename Function>
1696 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1697 Function&& aFunction) -> decltype(aFunction()) {
1698 static_assert(!std::is_lvalue_reference_v<Function>,
1699 "Function object must not be passed by lvalue-ref (to avoid "
1700 "unplanned copies); Consider move()ing the object.");
1701 return detail::InvokeAsync(aTarget, aCallerName,
1702 detail::AllowInvokeAsyncFunctionLVRef(),
1703 std::forward<Function>(aFunction));
1706 # undef PROMISE_LOG
1707 # undef PROMISE_ASSERT
1708 # undef PROMISE_DEBUG
1710 } // namespace mozilla
1712 #endif