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/. */
6 
7 #if !defined(MozPromise_h_)
8 #define MozPromise_h_
9 
10 #include "mozilla/AbstractThread.h"
11 #include "mozilla/IndexSequence.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/Mutex.h"
15 #include "mozilla/Monitor.h"
16 #include "mozilla/Tuple.h"
17 #include "mozilla/TypeTraits.h"
18 
19 #include "nsTArray.h"
20 #include "nsThreadUtils.h"
21 
22 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
23 #define PROMISE_DEBUG
24 #endif
25 
26 #ifdef PROMISE_DEBUG
27 #define PROMISE_ASSERT MOZ_RELEASE_ASSERT
28 #else
29 #define PROMISE_ASSERT(...) do { } while (0)
30 #endif
31 
32 namespace mozilla {
33 
34 extern LazyLogModule gMozPromiseLog;
35 
36 #define PROMISE_LOG(x, ...) \
37   MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
38 
39 namespace detail {
40 template<typename ThisType, typename Ret, typename ArgType>
41 static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType));
42 template<typename ThisType, typename Ret, typename ArgType>
43 static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType) const);
44 template<typename ThisType, typename Ret>
45 static FalseType TakesArgumentHelper(Ret (ThisType::*)());
46 template<typename ThisType, typename Ret>
47 static FalseType TakesArgumentHelper(Ret (ThisType::*)() const);
48 
49 template<typename ThisType, typename Ret, typename ArgType>
50 static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType));
51 template<typename ThisType, typename Ret, typename ArgType>
52 static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType) const);
53 template<typename ThisType, typename Ret>
54 static Ret ReturnTypeHelper(Ret (ThisType::*)());
55 template<typename ThisType, typename Ret>
56 static Ret ReturnTypeHelper(Ret (ThisType::*)() const);
57 
58 template<typename MethodType>
59 struct ReturnType {
60   typedef decltype(detail::ReturnTypeHelper(DeclVal<MethodType>())) Type;
61 };
62 
63 } // namespace detail
64 
65 template<typename MethodType>
66 struct TakesArgument {
67   static const bool value = decltype(detail::TakesArgumentHelper(DeclVal<MethodType>()))::value;
68 };
69 
70 template<typename MethodType, typename TargetType>
71 struct ReturnTypeIs {
72   static const bool value = IsConvertible<typename detail::ReturnType<MethodType>::Type, TargetType>::value;
73 };
74 
75 /*
76  * A promise manages an asynchronous request that may or may not be able to be
77  * fulfilled immediately. When an API returns a promise, the consumer may attach
78  * callbacks to be invoked (asynchronously, on a specified thread) when the
79  * request is either completed (resolved) or cannot be completed (rejected).
80  * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
81  * MozPromises resolution/rejection make a normal round-trip through the event
82  * loop, which simplifies their ordering semantics relative to other native code.
83  *
84  * MozPromises attempt to mirror the spirit of JS Promises to the extent that
85  * is possible (and desirable) in C++. While the intent is that MozPromises
86  * feel familiar to programmers who are accustomed to their JS-implemented cousin,
87  * we don't shy away from imposing restrictions and adding features that make
88  * sense for the use cases we encounter.
89  *
90  * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
91  * call accepts resolve and reject callbacks, and returns a MozPromise::Request.
92  * The Request object serves several purposes for the consumer.
93  *
94  *   (1) It allows the caller to cancel the delivery of the resolve/reject value
95  *       if it has not already occurred, via Disconnect() (this must be done on
96  *       the target thread to avoid racing).
97  *
98  *   (2) It provides access to a "Completion Promise", which is roughly analagous
99  *       to the Promise returned directly by ->then() calls on JS promises. If
100  *       the resolve/reject callback returns a new MozPromise, that promise is
101  *       chained to the completion promise, such that its resolve/reject value
102  *       will be forwarded along when it arrives. If the resolve/reject callback
103  *       returns void, the completion promise is resolved/rejected with the same
104  *       value that was passed to the callback.
105  *
106  * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
107  * (rather than already_AddRefed) from various methods. This is done to allow elegant
108  * chaining of calls without cluttering up the code with intermediate variables, and
109  * without introducing separate API variants for callers that want a return value
110  * (from, say, ->Then()) from those that don't.
111  *
112  * When IsExclusive is true, the MozPromise does a release-mode assertion that
113  * there is at most one call to either Then(...) or ChainTo(...).
114  */
115 
116 class MozPromiseRefcountable
117 {
118 public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)119   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
120 protected:
121   virtual ~MozPromiseRefcountable() {}
122 };
123 
124 template<typename T> class MozPromiseHolder;
125 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
126 class MozPromise : public MozPromiseRefcountable
127 {
128   static const uint32_t sMagic = 0xcecace11;
129 
130 public:
131   typedef ResolveValueT ResolveValueType;
132   typedef RejectValueT RejectValueType;
133   class ResolveOrRejectValue
134   {
135   public:
136     template<typename ResolveValueType_>
SetResolve(ResolveValueType_ && aResolveValue)137     void SetResolve(ResolveValueType_&& aResolveValue)
138     {
139       MOZ_ASSERT(IsNothing());
140       mResolveValue.emplace(Forward<ResolveValueType_>(aResolveValue));
141     }
142 
143     template<typename RejectValueType_>
SetReject(RejectValueType_ && aRejectValue)144     void SetReject(RejectValueType_&& aRejectValue)
145     {
146       MOZ_ASSERT(IsNothing());
147       mRejectValue.emplace(Forward<RejectValueType_>(aRejectValue));
148     }
149 
150     template<typename ResolveValueType_>
MakeResolve(ResolveValueType_ && aResolveValue)151     static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
152     {
153       ResolveOrRejectValue val;
154       val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
155       return val;
156     }
157 
158     template<typename RejectValueType_>
MakeReject(RejectValueType_ && aRejectValue)159     static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
160     {
161       ResolveOrRejectValue val;
162       val.SetReject(Forward<RejectValueType_>(aRejectValue));
163       return val;
164     }
165 
IsResolve()166     bool IsResolve() const { return mResolveValue.isSome(); }
IsReject()167     bool IsReject() const { return mRejectValue.isSome(); }
IsNothing()168     bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
169 
ResolveValue()170     const ResolveValueType& ResolveValue() const { return mResolveValue.ref(); }
RejectValue()171     const RejectValueType& RejectValue() const { return mRejectValue.ref(); }
172 
173   private:
174     Maybe<ResolveValueType> mResolveValue;
175     Maybe<RejectValueType> mRejectValue;
176   };
177 
178 protected:
179   // MozPromise is the public type, and never constructed directly. Construct
180   // a MozPromise::Private, defined below.
MozPromise(const char * aCreationSite,bool aIsCompletionPromise)181   MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
182     : mCreationSite(aCreationSite)
183     , mMutex("MozPromise Mutex")
184     , mHaveRequest(false)
185     , mIsCompletionPromise(aIsCompletionPromise)
186 #ifdef PROMISE_DEBUG
187     , mMagic4(mMutex.mLock)
188 #endif
189   {
190     PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
191   }
192 
193 public:
194   // MozPromise::Private allows us to separate the public interface (upon which
195   // consumers of the promise may invoke methods like Then()) from the private
196   // interface (upon which the creator of the promise may invoke Resolve() or
197   // Reject()). APIs should create and store a MozPromise::Private (usually
198   // via a MozPromiseHolder), and return a MozPromise to consumers.
199   //
200   // NB: We can include the definition of this class inline once B2G ICS is gone.
201   class Private;
202 
203   template<typename ResolveValueType_>
204   static RefPtr<MozPromise>
CreateAndResolve(ResolveValueType_ && aResolveValue,const char * aResolveSite)205   CreateAndResolve(ResolveValueType_&& aResolveValue, const char* aResolveSite)
206   {
207     RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
208     p->Resolve(Forward<ResolveValueType_>(aResolveValue), aResolveSite);
209     return p.forget();
210   }
211 
212   template<typename RejectValueType_>
213   static RefPtr<MozPromise>
CreateAndReject(RejectValueType_ && aRejectValue,const char * aRejectSite)214   CreateAndReject(RejectValueType_&& aRejectValue, const char* aRejectSite)
215   {
216     RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
217     p->Reject(Forward<RejectValueType_>(aRejectValue), aRejectSite);
218     return p.forget();
219   }
220 
221   typedef MozPromise<nsTArray<ResolveValueType>, RejectValueType, IsExclusive> AllPromiseType;
222 private:
223   class AllPromiseHolder : public MozPromiseRefcountable
224   {
225   public:
AllPromiseHolder(size_t aDependentPromises)226     explicit AllPromiseHolder(size_t aDependentPromises)
227       : mPromise(new typename AllPromiseType::Private(__func__))
228       , mOutstandingPromises(aDependentPromises)
229     {
230       mResolveValues.SetLength(aDependentPromises);
231     }
232 
Resolve(size_t aIndex,const ResolveValueType & aResolveValue)233     void Resolve(size_t aIndex, const ResolveValueType& aResolveValue)
234     {
235       if (!mPromise) {
236         // Already rejected.
237         return;
238       }
239 
240       mResolveValues[aIndex].emplace(aResolveValue);
241       if (--mOutstandingPromises == 0) {
242         nsTArray<ResolveValueType> resolveValues;
243         resolveValues.SetCapacity(mResolveValues.Length());
244         for (size_t i = 0; i < mResolveValues.Length(); ++i) {
245           resolveValues.AppendElement(mResolveValues[i].ref());
246         }
247 
248         mPromise->Resolve(resolveValues, __func__);
249         mPromise = nullptr;
250         mResolveValues.Clear();
251       }
252     }
253 
Reject(const RejectValueType & aRejectValue)254     void Reject(const RejectValueType& aRejectValue)
255     {
256       if (!mPromise) {
257         // Already rejected.
258         return;
259       }
260 
261       mPromise->Reject(aRejectValue, __func__);
262       mPromise = nullptr;
263       mResolveValues.Clear();
264     }
265 
Promise()266     AllPromiseType* Promise() { return mPromise; }
267 
268   private:
269     nsTArray<Maybe<ResolveValueType>> mResolveValues;
270     RefPtr<typename AllPromiseType::Private> mPromise;
271     size_t mOutstandingPromises;
272   };
273 public:
274 
All(AbstractThread * aProcessingThread,nsTArray<RefPtr<MozPromise>> & aPromises)275   static RefPtr<AllPromiseType> All(AbstractThread* aProcessingThread, nsTArray<RefPtr<MozPromise>>& aPromises)
276   {
277     RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
278     for (size_t i = 0; i < aPromises.Length(); ++i) {
279       aPromises[i]->Then(aProcessingThread, __func__,
280         [holder, i] (ResolveValueType aResolveValue) -> void { holder->Resolve(i, aResolveValue); },
281         [holder] (RejectValueType aRejectValue) -> void { holder->Reject(aRejectValue); }
282       );
283     }
284     return holder->Promise();
285   }
286 
287   class Request : public MozPromiseRefcountable
288   {
289   public:
290     virtual void Disconnect() = 0;
291 
292     virtual MozPromise* CompletionPromise() = 0;
293 
294     virtual void AssertIsDead() = 0;
295 
296   protected:
Request()297     Request() : mComplete(false), mDisconnected(false) {}
~Request()298     virtual ~Request() {}
299 
300     bool mComplete;
301     bool mDisconnected;
302   };
303 
304 protected:
305 
306   /*
307    * A ThenValue tracks a single consumer waiting on the promise. When a consumer
308    * invokes promise->Then(...), a ThenValue is created. Once the Promise is
309    * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
310    * invokes the resolve/reject method and then deletes the ThenValue.
311    */
312   class ThenValueBase : public Request
313   {
314     static const uint32_t sMagic = 0xfadece11;
315 
316   public:
317     class ResolveOrRejectRunnable : public Runnable
318     {
319     public:
ResolveOrRejectRunnable(ThenValueBase * aThenValue,MozPromise * aPromise)320       ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
321         : mThenValue(aThenValue)
322         , mPromise(aPromise)
323       {
324         MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
325       }
326 
~ResolveOrRejectRunnable()327       ~ResolveOrRejectRunnable()
328       {
329         if (mThenValue) {
330           mThenValue->AssertIsDead();
331         }
332       }
333 
Run()334       NS_IMETHOD Run() override
335       {
336         PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
337         mThenValue->DoResolveOrReject(mPromise->Value());
338         mThenValue = nullptr;
339         mPromise = nullptr;
340         return NS_OK;
341       }
342 
343     private:
344       RefPtr<ThenValueBase> mThenValue;
345       RefPtr<MozPromise> mPromise;
346     };
347 
ThenValueBase(AbstractThread * aResponseTarget,const char * aCallSite)348     explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
349       : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
350 
351 #ifdef PROMISE_DEBUG
~ThenValueBase()352     ~ThenValueBase()
353     {
354       mMagic1 = 0;
355       mMagic2 = 0;
356     }
357 #endif
358 
CompletionPromise()359     MozPromise* CompletionPromise() override
360     {
361       MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
362       MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
363       if (!mCompletionPromise) {
364         mCompletionPromise = new MozPromise::Private(
365           "<completion promise>", true /* aIsCompletionPromise */);
366       }
367       return mCompletionPromise;
368     }
369 
AssertIsDead()370     void AssertIsDead() override
371     {
372       PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
373       // We want to assert that this ThenValues is dead - that is to say, that
374       // there are no consumers waiting for the result. In the case of a normal
375       // ThenValue, we check that it has been disconnected, which is the way
376       // that the consumer signals that it no longer wishes to hear about the
377       // result. If this ThenValue has a completion promise (which is mutually
378       // exclusive with being disconnectable), we recursively assert that every
379       // ThenValue associated with the completion promise is dead.
380       if (mCompletionPromise) {
381         mCompletionPromise->AssertIsDead();
382       } else {
383         MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
384       }
385     }
386 
Dispatch(MozPromise * aPromise)387     void Dispatch(MozPromise *aPromise)
388     {
389       PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
390       aPromise->mMutex.AssertCurrentThreadOwns();
391       MOZ_ASSERT(!aPromise->IsPending());
392 
393       RefPtr<Runnable> runnable =
394         static_cast<Runnable*>(new (typename ThenValueBase::ResolveOrRejectRunnable)(this, aPromise));
395       PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
396                   aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
397                   runnable.get(), aPromise, this);
398 
399       // Promise consumers are allowed to disconnect the Request object and
400       // then shut down the thread or task queue that the promise result would
401       // be dispatched on. So we unfortunately can't assert that promise
402       // dispatch succeeds. :-(
403       mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess);
404     }
405 
Disconnect()406     virtual void Disconnect() override
407     {
408       MOZ_DIAGNOSTIC_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
409       MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
410       Request::mDisconnected = true;
411 
412       // We could support rejecting the completion promise on disconnection, but
413       // then we'd need to have some sort of default reject value. The use cases
414       // of disconnection and completion promise chaining seem pretty orthogonal,
415       // so let's use assert against it.
416       MOZ_DIAGNOSTIC_ASSERT(!mCompletionPromise);
417     }
418 
419   protected:
420     virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) = 0;
421 
DoResolveOrReject(const ResolveOrRejectValue & aValue)422     void DoResolveOrReject(const ResolveOrRejectValue& aValue)
423     {
424       PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
425       MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
426       Request::mComplete = true;
427       if (Request::mDisconnected) {
428         PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
429         return;
430       }
431 
432       // Invoke the resolve or reject method.
433       RefPtr<MozPromise> p = DoResolveOrRejectInternal(aValue);
434 
435       // If there's a completion promise, resolve it appropriately with the
436       // result of the method.
437       //
438       // We jump through some hoops to cast to MozPromise::Private here. This
439       // can go away when we can just declare mCompletionPromise as
440       // MozPromise::Private. See the declaration below.
441       RefPtr<MozPromise::Private> completionPromise =
442         dont_AddRef(static_cast<MozPromise::Private*>(mCompletionPromise.forget().take()));
443       if (completionPromise) {
444         if (p) {
445           p->ChainTo(completionPromise.forget(), "<chained completion promise>");
446         } else {
447           completionPromise->ResolveOrReject(aValue, "<completion of non-promise-returning method>");
448         }
449       }
450     }
451 
452     RefPtr<AbstractThread> mResponseTarget; // May be released on any thread.
453 #ifdef PROMISE_DEBUG
454     uint32_t mMagic1 = sMagic;
455 #endif
456     // Declaring RefPtr<MozPromise::Private> here causes build failures
457     // on MSVC because MozPromise::Private is only forward-declared at this
458     // point. This hack can go away when we inline-declare MozPromise::Private,
459     // which is blocked on the B2G ICS compiler being too old.
460     RefPtr<MozPromise> mCompletionPromise;
461 #ifdef PROMISE_DEBUG
462     uint32_t mMagic2 = sMagic;
463 #endif
464     const char* mCallSite;
465   };
466 
467   /*
468    * We create two overloads for invoking Resolve/Reject Methods so as to
469    * make the resolve/reject value argument "optional".
470    */
471 
472   template<typename ThisType, typename MethodType, typename ValueType>
473   static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
474                            TakesArgument<MethodType>::value,
475                            already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType * aThisVal,MethodType aMethod,ValueType && aValue)476   InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
477   {
478     return ((*aThisVal).*aMethod)(Forward<ValueType>(aValue)).forget();
479   }
480 
481   template<typename ThisType, typename MethodType, typename ValueType>
482   static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
483                            TakesArgument<MethodType>::value,
484                            already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType * aThisVal,MethodType aMethod,ValueType && aValue)485   InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
486   {
487     ((*aThisVal).*aMethod)(Forward<ValueType>(aValue));
488     return nullptr;
489   }
490 
491   template<typename ThisType, typename MethodType, typename ValueType>
492   static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
493                            !TakesArgument<MethodType>::value,
494                            already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType * aThisVal,MethodType aMethod,ValueType && aValue)495   InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
496   {
497     return ((*aThisVal).*aMethod)().forget();
498   }
499 
500   template<typename ThisType, typename MethodType, typename ValueType>
501   static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
502                            !TakesArgument<MethodType>::value,
503                            already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType * aThisVal,MethodType aMethod,ValueType && aValue)504   InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
505   {
506     ((*aThisVal).*aMethod)();
507     return nullptr;
508   }
509 
510   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
511   class MethodThenValue : public ThenValueBase
512   {
513   public:
MethodThenValue(AbstractThread * aResponseTarget,ThisType * aThisVal,ResolveMethodType aResolveMethod,RejectMethodType aRejectMethod,const char * aCallSite)514     MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
515                     ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
516                     const char* aCallSite)
517       : ThenValueBase(aResponseTarget, aCallSite)
518       , mThisVal(aThisVal)
519       , mResolveMethod(aResolveMethod)
520       , mRejectMethod(aRejectMethod) {}
521 
Disconnect()522   virtual void Disconnect() override
523   {
524     ThenValueBase::Disconnect();
525 
526     // If a Request has been disconnected, we don't guarantee that the
527     // resolve/reject runnable will be dispatched. Null out our refcounted
528     // this-value now so that it's released predictably on the dispatch thread.
529     mThisVal = nullptr;
530   }
531 
532   protected:
DoResolveOrRejectInternal(const ResolveOrRejectValue & aValue)533     virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
534     {
535       RefPtr<MozPromise> completion;
536       if (aValue.IsResolve()) {
537         completion = InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
538       } else {
539         completion = InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
540       }
541 
542       // Null out mThisVal after invoking the callback so that any references are
543       // released predictably on the dispatch thread. Otherwise, it would be
544       // released on whatever thread last drops its reference to the ThenValue,
545       // which may or may not be ok.
546       mThisVal = nullptr;
547 
548       return completion.forget();
549     }
550 
551   private:
552     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
553     ResolveMethodType mResolveMethod;
554     RejectMethodType mRejectMethod;
555   };
556 
557   // NB: We could use std::function here instead of a template if it were supported. :-(
558   template<typename ResolveFunction, typename RejectFunction>
559   class FunctionThenValue : public ThenValueBase
560   {
561   public:
FunctionThenValue(AbstractThread * aResponseTarget,ResolveFunction && aResolveFunction,RejectFunction && aRejectFunction,const char * aCallSite)562     FunctionThenValue(AbstractThread* aResponseTarget,
563                       ResolveFunction&& aResolveFunction,
564                       RejectFunction&& aRejectFunction,
565                       const char* aCallSite)
566       : ThenValueBase(aResponseTarget, aCallSite)
567     {
568       mResolveFunction.emplace(Move(aResolveFunction));
569       mRejectFunction.emplace(Move(aRejectFunction));
570     }
571 
Disconnect()572   virtual void Disconnect() override
573   {
574     ThenValueBase::Disconnect();
575 
576     // If a Request has been disconnected, we don't guarantee that the
577     // resolve/reject runnable will be dispatched. Destroy our callbacks
578     // now so that any references in closures are released predictable on
579     // the dispatch thread.
580     mResolveFunction.reset();
581     mRejectFunction.reset();
582   }
583 
584   protected:
DoResolveOrRejectInternal(const ResolveOrRejectValue & aValue)585     virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
586     {
587       // Note: The usage of InvokeCallbackMethod here requires that
588       // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
589       // classes with ::operator()), since it allows us to share code more easily.
590       // We could fix this if need be, though it's quite easy to work around by
591       // just capturing something.
592       RefPtr<MozPromise> completion;
593       if (aValue.IsResolve()) {
594         completion = InvokeCallbackMethod(mResolveFunction.ptr(), &ResolveFunction::operator(), aValue.ResolveValue());
595       } else {
596         completion = InvokeCallbackMethod(mRejectFunction.ptr(), &RejectFunction::operator(), aValue.RejectValue());
597       }
598 
599       // Destroy callbacks after invocation so that any references in closures are
600       // released predictably on the dispatch thread. Otherwise, they would be
601       // released on whatever thread last drops its reference to the ThenValue,
602       // which may or may not be ok.
603       mResolveFunction.reset();
604       mRejectFunction.reset();
605 
606       return completion.forget();
607     }
608 
609   private:
610     Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
611     Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
612   };
613 
614 public:
ThenInternal(AbstractThread * aResponseThread,ThenValueBase * aThenValue,const char * aCallSite)615   void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue,
616                     const char* aCallSite)
617   {
618     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
619     MutexAutoLock lock(mMutex);
620     MOZ_ASSERT(aResponseThread->IsDispatchReliable());
621     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
622     mHaveRequest = true;
623     PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
624                 aCallSite, this, aThenValue, (int) IsPending());
625     if (!IsPending()) {
626       aThenValue->Dispatch(this);
627     } else {
628       mThenValues.AppendElement(aThenValue);
629     }
630   }
631 
632 public:
633 
634   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
Then(AbstractThread * aResponseThread,const char * aCallSite,ThisType * aThisVal,ResolveMethodType aResolveMethod,RejectMethodType aRejectMethod)635   RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
636                          ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
637   {
638     RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
639                                               aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
640     ThenInternal(aResponseThread, thenValue, aCallSite);
641     return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
642   }
643 
644   template<typename ResolveFunction, typename RejectFunction>
Then(AbstractThread * aResponseThread,const char * aCallSite,ResolveFunction && aResolveFunction,RejectFunction && aRejectFunction)645   RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
646                          ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
647   {
648     RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
649                                               Move(aResolveFunction), Move(aRejectFunction), aCallSite);
650     ThenInternal(aResponseThread, thenValue, aCallSite);
651     return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
652   }
653 
ChainTo(already_AddRefed<Private> aChainedPromise,const char * aCallSite)654   void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
655   {
656     MutexAutoLock lock(mMutex);
657     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
658     mHaveRequest = true;
659     RefPtr<Private> chainedPromise = aChainedPromise;
660     PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
661                 aCallSite, this, chainedPromise.get(), (int) IsPending());
662     if (!IsPending()) {
663       ForwardTo(chainedPromise);
664     } else {
665       mChainedPromises.AppendElement(chainedPromise);
666     }
667   }
668 
669   // Note we expose the function AssertIsDead() instead of IsDead() since
670   // checking IsDead() is a data race in the situation where the request is not
671   // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
672   // AssertIsDead() only.
AssertIsDead()673   void AssertIsDead()
674   {
675     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
676     MutexAutoLock lock(mMutex);
677     for (auto&& then : mThenValues) {
678       then->AssertIsDead();
679     }
680     for (auto&& chained : mChainedPromises) {
681       chained->AssertIsDead();
682     }
683   }
684 
685 protected:
IsPending()686   bool IsPending() const { return mValue.IsNothing(); }
Value()687   const ResolveOrRejectValue& Value() const
688   {
689     // This method should only be called once the value has stabilized. As
690     // such, we don't need to acquire the lock here.
691     MOZ_DIAGNOSTIC_ASSERT(!IsPending());
692     return mValue;
693   }
694 
DispatchAll()695   void DispatchAll()
696   {
697     mMutex.AssertCurrentThreadOwns();
698     for (size_t i = 0; i < mThenValues.Length(); ++i) {
699       mThenValues[i]->Dispatch(this);
700     }
701     mThenValues.Clear();
702 
703     for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
704       ForwardTo(mChainedPromises[i]);
705     }
706     mChainedPromises.Clear();
707   }
708 
ForwardTo(Private * aOther)709   void ForwardTo(Private* aOther)
710   {
711     MOZ_ASSERT(!IsPending());
712     if (mValue.IsResolve()) {
713       aOther->Resolve(mValue.ResolveValue(), "<chained promise>");
714     } else {
715       aOther->Reject(mValue.RejectValue(), "<chained promise>");
716     }
717   }
718 
~MozPromise()719   virtual ~MozPromise()
720   {
721     PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
722     AssertIsDead();
723     // We can't guarantee a completion promise will always be revolved or
724     // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
725     if (!mIsCompletionPromise) {
726       MOZ_ASSERT(!IsPending());
727       MOZ_ASSERT(mThenValues.IsEmpty());
728       MOZ_ASSERT(mChainedPromises.IsEmpty());
729     }
730 #ifdef PROMISE_DEBUG
731     mMagic1 = 0;
732     mMagic2 = 0;
733     mMagic3 = 0;
734     mMagic4 = nullptr;
735 #endif
736   };
737 
738   const char* mCreationSite; // For logging
739   Mutex mMutex;
740   ResolveOrRejectValue mValue;
741 #ifdef PROMISE_DEBUG
742   uint32_t mMagic1 = sMagic;
743 #endif
744   nsTArray<RefPtr<ThenValueBase>> mThenValues;
745 #ifdef PROMISE_DEBUG
746   uint32_t mMagic2 = sMagic;
747 #endif
748   nsTArray<RefPtr<Private>> mChainedPromises;
749 #ifdef PROMISE_DEBUG
750   uint32_t mMagic3 = sMagic;
751 #endif
752   bool mHaveRequest;
753   const bool mIsCompletionPromise;
754 #ifdef PROMISE_DEBUG
755   void* mMagic4;
756 #endif
757 };
758 
759 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
760 class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
761   : public MozPromise<ResolveValueT, RejectValueT, IsExclusive>
762 {
763 public:
764   explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
MozPromise(aCreationSite,aIsCompletionPromise)765     : MozPromise(aCreationSite, aIsCompletionPromise) {}
766 
767   template<typename ResolveValueT_>
Resolve(ResolveValueT_ && aResolveValue,const char * aResolveSite)768   void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite)
769   {
770     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
771     MutexAutoLock lock(mMutex);
772     MOZ_ASSERT(IsPending());
773     PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
774     mValue.SetResolve(Forward<ResolveValueT_>(aResolveValue));
775     DispatchAll();
776   }
777 
778   template<typename RejectValueT_>
Reject(RejectValueT_ && aRejectValue,const char * aRejectSite)779   void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite)
780   {
781     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
782     MutexAutoLock lock(mMutex);
783     MOZ_ASSERT(IsPending());
784     PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
785     mValue.SetReject(Forward<RejectValueT_>(aRejectValue));
786     DispatchAll();
787   }
788 
789   template<typename ResolveOrRejectValue_>
ResolveOrReject(ResolveOrRejectValue_ && aValue,const char * aSite)790   void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite)
791   {
792     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
793     MutexAutoLock lock(mMutex);
794     MOZ_ASSERT(IsPending());
795     PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite, this, mCreationSite);
796     mValue = Forward<ResolveOrRejectValue_>(aValue);
797     DispatchAll();
798   }
799 };
800 
801 // A generic promise type that does the trick for simple use cases.
802 typedef MozPromise<bool, nsresult, /* IsExclusive = */ false> GenericPromise;
803 
804 /*
805  * Class to encapsulate a promise for a particular role. Use this as the member
806  * variable for a class whose method returns a promise.
807  */
808 template<typename PromiseType>
809 class MozPromiseHolder
810 {
811 public:
MozPromiseHolder()812   MozPromiseHolder()
813     : mMonitor(nullptr) {}
814 
815   // Move semantics.
816   MozPromiseHolder& operator=(MozPromiseHolder&& aOther)
817   {
818     MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
819     MOZ_DIAGNOSTIC_ASSERT(!mPromise);
820     mPromise = aOther.mPromise;
821     aOther.mPromise = nullptr;
822     return *this;
823   }
824 
~MozPromiseHolder()825   ~MozPromiseHolder() { MOZ_ASSERT(!mPromise); }
826 
Ensure(const char * aMethodName)827   already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
828     if (mMonitor) {
829       mMonitor->AssertCurrentThreadOwns();
830     }
831     if (!mPromise) {
832       mPromise = new (typename PromiseType::Private)(aMethodName);
833     }
834     RefPtr<PromiseType> p = mPromise.get();
835     return p.forget();
836   }
837 
838   // Provide a Monitor that should always be held when accessing this instance.
SetMonitor(Monitor * aMonitor)839   void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
840 
IsEmpty()841   bool IsEmpty() const
842   {
843     if (mMonitor) {
844       mMonitor->AssertCurrentThreadOwns();
845     }
846     return !mPromise;
847   }
848 
Steal()849   already_AddRefed<typename PromiseType::Private> Steal()
850   {
851     if (mMonitor) {
852       mMonitor->AssertCurrentThreadOwns();
853     }
854 
855     RefPtr<typename PromiseType::Private> p = mPromise;
856     mPromise = nullptr;
857     return p.forget();
858   }
859 
Resolve(typename PromiseType::ResolveValueType aResolveValue,const char * aMethodName)860   void Resolve(typename PromiseType::ResolveValueType aResolveValue,
861                const char* aMethodName)
862   {
863     if (mMonitor) {
864       mMonitor->AssertCurrentThreadOwns();
865     }
866     MOZ_ASSERT(mPromise);
867     mPromise->Resolve(aResolveValue, aMethodName);
868     mPromise = nullptr;
869   }
870 
871 
ResolveIfExists(typename PromiseType::ResolveValueType aResolveValue,const char * aMethodName)872   void ResolveIfExists(typename PromiseType::ResolveValueType aResolveValue,
873                        const char* aMethodName)
874   {
875     if (!IsEmpty()) {
876       Resolve(aResolveValue, aMethodName);
877     }
878   }
879 
Reject(typename PromiseType::RejectValueType aRejectValue,const char * aMethodName)880   void Reject(typename PromiseType::RejectValueType aRejectValue,
881               const char* aMethodName)
882   {
883     if (mMonitor) {
884       mMonitor->AssertCurrentThreadOwns();
885     }
886     MOZ_ASSERT(mPromise);
887     mPromise->Reject(aRejectValue, aMethodName);
888     mPromise = nullptr;
889   }
890 
891 
RejectIfExists(typename PromiseType::RejectValueType aRejectValue,const char * aMethodName)892   void RejectIfExists(typename PromiseType::RejectValueType aRejectValue,
893                       const char* aMethodName)
894   {
895     if (!IsEmpty()) {
896       Reject(aRejectValue, aMethodName);
897     }
898   }
899 
900 private:
901   Monitor* mMonitor;
902   RefPtr<typename PromiseType::Private> mPromise;
903 };
904 
905 /*
906  * Class to encapsulate a MozPromise::Request reference. Use this as the member
907  * variable for a class waiting on a MozPromise.
908  */
909 template<typename PromiseType>
910 class MozPromiseRequestHolder
911 {
912 public:
MozPromiseRequestHolder()913   MozPromiseRequestHolder() {}
~MozPromiseRequestHolder()914   ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
915 
Begin(RefPtr<typename PromiseType::Request> && aRequest)916   void Begin(RefPtr<typename PromiseType::Request>&& aRequest)
917   {
918     MOZ_DIAGNOSTIC_ASSERT(!Exists());
919     mRequest = Move(aRequest);
920   }
921 
Begin(typename PromiseType::Request * aRequest)922   void Begin(typename PromiseType::Request* aRequest)
923   {
924     MOZ_DIAGNOSTIC_ASSERT(!Exists());
925     mRequest = aRequest;
926   }
927 
Complete()928   void Complete()
929   {
930     MOZ_DIAGNOSTIC_ASSERT(Exists());
931     mRequest = nullptr;
932   }
933 
934   // Disconnects and forgets an outstanding promise. The resolve/reject methods
935   // will never be called.
Disconnect()936   void Disconnect() {
937     MOZ_ASSERT(Exists());
938     mRequest->Disconnect();
939     mRequest = nullptr;
940   }
941 
DisconnectIfExists()942   void DisconnectIfExists() {
943     if (Exists()) {
944       Disconnect();
945     }
946   }
947 
Exists()948   bool Exists() const { return !!mRequest; }
949 
950 private:
951   RefPtr<typename PromiseType::Request> mRequest;
952 };
953 
954 // Asynchronous Potentially-Cross-Thread Method Calls.
955 //
956 // This machinery allows callers to schedule a promise-returning method to be
957 // invoked asynchronously on a given thread, while at the same time receiving
958 // a promise upon which to invoke Then() immediately. InvokeAsync dispatches
959 // a task to invoke the method on the proper thread and also chain the resulting
960 // promise to the one that the caller received, so that resolve/reject values
961 // are forwarded through.
962 
963 namespace detail {
964 
965 template<typename ReturnType, typename ThisType, typename... ArgTypes, size_t... Indices>
966 ReturnType
MethodCallInvokeHelper(ReturnType (ThisType::* aMethod)(ArgTypes...),ThisType * aThisVal,Tuple<ArgTypes...> & aArgs,IndexSequence<Indices...>)967 MethodCallInvokeHelper(ReturnType(ThisType::*aMethod)(ArgTypes...), ThisType* aThisVal,
968                        Tuple<ArgTypes...>& aArgs, IndexSequence<Indices...>)
969 {
970   return ((*aThisVal).*aMethod)(Get<Indices>(aArgs)...);
971 }
972 
973 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
974 // assertions when used on templated types.
975 class MethodCallBase
976 {
977 public:
MethodCallBase()978   MethodCallBase() { MOZ_COUNT_CTOR(MethodCallBase); }
~MethodCallBase()979   virtual ~MethodCallBase() { MOZ_COUNT_DTOR(MethodCallBase); }
980 };
981 
982 template<typename PromiseType, typename ThisType, typename... ArgTypes>
983 class MethodCall : public MethodCallBase
984 {
985 public:
986   typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
MethodCall(MethodType aMethod,ThisType * aThisVal,ArgTypes...aArgs)987   MethodCall(MethodType aMethod, ThisType* aThisVal, ArgTypes... aArgs)
988     : mMethod(aMethod)
989     , mThisVal(aThisVal)
990     , mArgs(Forward<ArgTypes>(aArgs)...)
991   {}
992 
Invoke()993   RefPtr<PromiseType> Invoke()
994   {
995     return MethodCallInvokeHelper(mMethod, mThisVal.get(), mArgs, typename IndexSequenceFor<ArgTypes...>::Type());
996   }
997 
998 private:
999   MethodType mMethod;
1000   RefPtr<ThisType> mThisVal;
1001   Tuple<ArgTypes...> mArgs;
1002 };
1003 
1004 template<typename PromiseType, typename ThisType, typename ...ArgTypes>
1005 class ProxyRunnable : public Runnable
1006 {
1007 public:
ProxyRunnable(typename PromiseType::Private * aProxyPromise,MethodCall<PromiseType,ThisType,ArgTypes...> * aMethodCall)1008   ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCall<PromiseType, ThisType, ArgTypes...>* aMethodCall)
1009     : mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {}
1010 
Run()1011   NS_IMETHOD Run() override
1012   {
1013     RefPtr<PromiseType> p = mMethodCall->Invoke();
1014     mMethodCall = nullptr;
1015     p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1016     return NS_OK;
1017   }
1018 
1019 private:
1020   RefPtr<typename PromiseType::Private> mProxyPromise;
1021   nsAutoPtr<MethodCall<PromiseType, ThisType, ArgTypes...>> mMethodCall;
1022 };
1023 
Any()1024 constexpr bool Any()
1025 {
1026   return false;
1027 }
1028 
1029 template <typename T1>
Any(T1 a)1030 constexpr bool Any(T1 a)
1031 {
1032   return static_cast<bool>(a);
1033 }
1034 
1035 template <typename T1, typename... Ts>
Any(T1 a,Ts...aOthers)1036 constexpr bool Any(T1 a, Ts... aOthers)
1037 {
1038   return a || Any(aOthers...);
1039 }
1040 
1041 } // namespace detail
1042 
1043 template<typename PromiseType, typename ThisType, typename ...ArgTypes, typename ...ActualArgTypes>
1044 static RefPtr<PromiseType>
InvokeAsync(AbstractThread * aTarget,ThisType * aThisVal,const char * aCallerName,RefPtr<PromiseType> (ThisType::* aMethod)(ArgTypes...),ActualArgTypes &&...aArgs)1045 InvokeAsync(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
1046             RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...), ActualArgTypes&&... aArgs)
1047 {
1048   static_assert(!detail::Any(IsReference<ArgTypes>::value...),
1049                 "Cannot pass reference types through InvokeAsync, see bug 1313497 if you require it");
1050   typedef detail::MethodCall<PromiseType, ThisType, ArgTypes...> MethodCallType;
1051   typedef detail::ProxyRunnable<PromiseType, ThisType, ArgTypes...> ProxyRunnableType;
1052 
1053   MethodCallType* methodCall = new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
1054   RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
1055   RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
1056   MOZ_ASSERT(aTarget->IsDispatchReliable());
1057   aTarget->Dispatch(r.forget());
1058   return p.forget();
1059 }
1060 
1061 #undef PROMISE_LOG
1062 #undef PROMISE_ASSERT
1063 #undef PROMISE_DEBUG
1064 
1065 } // namespace mozilla
1066 
1067 #endif
1068