Bug 1321250 - Add MozPromise::ThenPromise() for easier promise chaining. r=jya
authorJW Wang <jwwang@mozilla.com>
Wed, 30 Nov 2016 18:23:12 +0800
changeset 324937 0fc28aab9031aa803f8adbe69c059063195cd6c3
parent 324936 fcabe39c665ff1e8e5a83563748449ca1acc214f
child 324938 7d0ff0aae0a0b99a26ae1236817dabba93f53e49
push id84553
push userryanvm@gmail.com
push dateThu, 01 Dec 2016 14:33:55 +0000
treeherdermozilla-inbound@07b9aab24f30 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1321250
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1321250 - Add MozPromise::ThenPromise() for easier promise chaining. r=jya MozReview-Commit-ID: 7J60CN0HbOW
xpcom/threads/MozPromise.h
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -327,24 +327,35 @@ protected:
         return NS_OK;
       }
 
     private:
       RefPtr<ThenValueBase> mThenValue;
       RefPtr<MozPromise> mPromise;
     };
 
-    explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
-      : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
+    ThenValueBase(AbstractThread* aResponseTarget,
+                  const char* aCallSite,
+                  bool aInitCompletionPromise = false)
+      : mResponseTarget(aResponseTarget)
+      , mCallSite(aCallSite)
+      , mInitCompletionPromise(aInitCompletionPromise)
+    {
+      if (mInitCompletionPromise) {
+        mCompletionPromise = new MozPromise::Private(
+          "<completion promise>", true /* aIsCompletionPromise */);
+      }
+    }
 
     MozPromise* CompletionPromise() override
     {
-      MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
+      MOZ_DIAGNOSTIC_ASSERT(mInitCompletionPromise ||
+                            mResponseTarget->IsCurrentThreadIn());
       MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
-      if (!mCompletionPromise) {
+      if (!mInitCompletionPromise && !mCompletionPromise) {
         mCompletionPromise = new MozPromise::Private(
           "<completion promise>", true /* aIsCompletionPromise */);
       }
       return mCompletionPromise;
     }
 
     void AssertIsDead() override
     {
@@ -428,16 +439,20 @@ protected:
 
     // Declaring RefPtr<MozPromise::Private> here causes build failures
     // on MSVC because MozPromise::Private is only forward-declared at this
     // point. This hack can go away when we inline-declare MozPromise::Private,
     // which is blocked on the B2G ICS compiler being too old.
     RefPtr<MozPromise> mCompletionPromise;
 
     const char* mCallSite;
+
+    // True if mCompletionPromise should be initialized in the constructor
+    // to make CompletionPromise() thread-safe.
+    const bool mInitCompletionPromise;
   };
 
   /*
    * We create two overloads for invoking Resolve/Reject Methods so as to
    * make the resolve/reject value argument "optional".
    */
 
   template<typename ThisType, typename MethodType, typename ValueType>
@@ -479,18 +494,18 @@ protected:
   }
 
   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
   class MethodThenValue : public ThenValueBase
   {
   public:
     MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
                     ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
-                    const char* aCallSite)
-      : ThenValueBase(aResponseTarget, aCallSite)
+                    const char* aCallSite, bool aInitCompletionPromise = false)
+      : ThenValueBase(aResponseTarget, aCallSite, aInitCompletionPromise)
       , mThisVal(aThisVal)
       , mResolveMethod(aResolveMethod)
       , mRejectMethod(aRejectMethod) {}
 
   virtual void Disconnect() override
   {
     ThenValueBase::Disconnect();
 
@@ -528,18 +543,19 @@ protected:
   // NB: We could use std::function here instead of a template if it were supported. :-(
   template<typename ResolveFunction, typename RejectFunction>
   class FunctionThenValue : public ThenValueBase
   {
   public:
     FunctionThenValue(AbstractThread* aResponseTarget,
                       ResolveFunction&& aResolveFunction,
                       RejectFunction&& aRejectFunction,
-                      const char* aCallSite)
-      : ThenValueBase(aResponseTarget, aCallSite)
+                      const char* aCallSite,
+                      bool aInitCompletionPromise = false)
+      : ThenValueBase(aResponseTarget, aCallSite, aInitCompletionPromise)
     {
       mResolveFunction.emplace(Move(aResolveFunction));
       mRejectFunction.emplace(Move(aRejectFunction));
     }
 
   virtual void Disconnect() override
   {
     ThenValueBase::Disconnect();
@@ -598,34 +614,69 @@ public:
       mThenValues.AppendElement(aThenValue);
     }
   }
 
 public:
 
   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
   RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
-                         ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+                       ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
   {
     RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
                                               aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
     ThenInternal(aResponseThread, thenValue, aCallSite);
     return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
   }
 
   template<typename ResolveFunction, typename RejectFunction>
   RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
-                         ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+                       ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
   {
     RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
                                               Move(aResolveFunction), Move(aRejectFunction), aCallSite);
     ThenInternal(aResponseThread, thenValue, aCallSite);
     return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
   }
 
+  // Equivalent to Then(target, ...)->CompletionPromise()
+  // without the restriction that CompletionPromise() must be called on the
+  // |target| thread. So ThenPromise() can be called on any thread as Then().
+  // The syntax is close to JS promise and makes promise chaining easier
+  // where you can do: p->ThenPromise()->ThenPromise()->ThenPromise();
+  //
+  // Note you would have to call Then() instead when the result needs to be held
+  // by a MozPromiseRequestHolder for future disconnection.
+  //
+  // TODO: replace Then()->CompletionPromise() with ThenPromise() and
+  // stop exposing CompletionPromise() to the client code.
+  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+  MOZ_MUST_USE RefPtr<MozPromise>
+  ThenPromise(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
+              ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+  {
+    using ThenType = MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>;
+    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread, aThisVal, aResolveMethod,
+      aRejectMethod, aCallSite, true /* aInitCompletionPromise */);
+    ThenInternal(aResponseThread, thenValue, aCallSite);
+    return thenValue->CompletionPromise();
+  }
+
+  template<typename ResolveFunction, typename RejectFunction>
+  MOZ_MUST_USE RefPtr<MozPromise>
+  ThenPromise(AbstractThread* aResponseThread, const char* aCallSite,
+              ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+  {
+    using ThenType = FunctionThenValue<ResolveFunction, RejectFunction>;
+    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread, Move(aResolveFunction),
+      Move(aRejectFunction), aCallSite, true /* aInitCompletionPromise */);
+    ThenInternal(aResponseThread, thenValue, aCallSite);
+    return thenValue->CompletionPromise();
+  }
+
   void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
     mHaveRequest = true;
     RefPtr<Private> chainedPromise = aChainedPromise;
     PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
                 aCallSite, this, chainedPromise.get(), (int) IsPending());