Bug 1321744. Part 1 - re-implement MozPromise::Then() using the command pattern. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 02 Dec 2016 15:41:19 +0800
changeset 448940 a8c1c34728687bfd3d9e653b40ff688f4c0e384f
parent 448935 10d86ab4330103ba21ddf4e7e032bd1a9802474b
child 448941 76542594d44cdadc0b35f4d3d3335b1cf2a58192
push id38491
push userjwwang@mozilla.com
push dateTue, 13 Dec 2016 09:13:24 +0000
bugs1321744
milestone53.0a1
Bug 1321744. Part 1 - re-implement MozPromise::Then() using the command pattern. MozReview-Commit-ID: 2FkjtBKjIAc
xpcom/threads/MozPromise.h
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -588,36 +588,16 @@ public:
       aThenValue->Dispatch(this);
     } else {
       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)
-  {
-    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)
-  {
-    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>.
-  }
-
   // 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.
   template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
   MOZ_MUST_USE RefPtr<MozPromise>
@@ -650,16 +630,87 @@ public:
       "<completion promise>", true /* aIsCompletionPromise */);
     thenValue->mCompletionPromise = p;
     // Note ThenInternal() might nullify mCompletionPromise before return.
     // So we need to return p instead of mCompletionPromise.
     ThenInternal(aResponseThread, thenValue, aCallSite);
     return p;
   }
 
+private:
+  /*
+   * A command object to store all information needed to make a request to
+   * the promise. This allows us to delay the request until further use is
+   * known (whether it is ->Then() again for more promise chaining or passed
+   * to MozPromiseRequestHolder::Begin() to terminate chaining and issue
+   * the request).
+   *
+   * This allows a unified syntax for promise chaining and disconnection
+   * and feels more like its JS counterpart.
+   */
+  class ThenCommand
+  {
+    friend class MozPromise;
+
+    ThenCommand(AbstractThread* aResponseThread,
+                const char* aCallSite,
+                already_AddRefed<ThenValueBase> aThenValue,
+                MozPromise* aReceiver)
+      : mResponseThread(aResponseThread)
+      , mCallSite(aCallSite)
+      , mThenValue(aThenValue)
+      , mReceiver(aReceiver) {}
+
+    ThenCommand(ThenCommand&& aOther) = default;
+
+  public:
+    ~ThenCommand()
+    {
+      // Issue the request now if the return value of Then() is not used.
+      if (mThenValue) {
+        mReceiver->ThenInternal(mResponseThread, mThenValue, mCallSite);
+      }
+    }
+
+    // Allow passing Then() to MozPromiseRequestHolder::Begin().
+    operator RefPtr<Request>()
+    {
+      RefPtr<ThenValueBase> thenValue = mThenValue.forget();
+      mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
+      return thenValue.forget();
+    }
+
+  private:
+    AbstractThread* mResponseThread;
+    const char* mCallSite;
+    RefPtr<ThenValueBase> mThenValue;
+    MozPromise* mReceiver;
+  };
+
+  public:
+  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+  ThenCommand Then(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);
+    return ThenCommand(aResponseThread, aCallSite, thenValue.forget(), this);
+  }
+
+  template<typename ResolveFunction, typename RejectFunction>
+  ThenCommand Then(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);
+    return ThenCommand(aResponseThread, aCallSite, thenValue.forget(), this);
+  }
+
   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());