Bug 1161742 - Introduce ResolveOrRejectValue to make MediaPromise implementation less verbose. r=jww
authorBobby Holley <bobbyholley@gmail.com>
Tue, 05 May 2015 14:34:58 -0700
changeset 273878 7636ab3485da8d7e275917228932877d217c8e94
parent 273877 9e84d753525ff1e66384b0ece51d4b4a6f3c4b18
child 273879 e3b6d0d1adeade47d5b22212e4f29092a83b920f
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjww
bugs1161742
milestone40.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 1161742 - Introduce ResolveOrRejectValue to make MediaPromise implementation less verbose. r=jww
dom/media/MediaPromise.h
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -44,16 +44,41 @@ extern PRLogModuleInfo* gMediaPromiseLog
  */
 template<typename T> class MediaPromiseHolder;
 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 class MediaPromise
 {
 public:
   typedef ResolveValueT ResolveValueType;
   typedef RejectValueT RejectValueType;
+  class ResolveOrRejectValue
+  {
+  public:
+    void SetResolve(ResolveValueType& aResolveValue)
+    {
+      MOZ_ASSERT(IsNothing());
+      mResolveValue.emplace(aResolveValue);
+    }
+
+    void SetReject(RejectValueType& aRejectValue)
+    {
+      MOZ_ASSERT(IsNothing());
+      mRejectValue.emplace(aRejectValue);
+    }
+
+    bool IsResolve() const { return mResolveValue.isSome(); }
+    bool IsReject() const { return mRejectValue.isSome(); }
+    bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
+    ResolveValueType& ResolveValue() { return mResolveValue.ref(); }
+    RejectValueType& RejectValue() { return mRejectValue.ref(); }
+
+  private:
+    Maybe<ResolveValueType> mResolveValue;
+    Maybe<RejectValueType> mRejectValue;
+  };
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise)
 
 protected:
   // MediaPromise is the public type, and never constructed directly. Construct
   // a MediaPromise::Private, defined below.
   explicit MediaPromise(const char* aCreationSite)
     : mCreationSite(aCreationSite)
@@ -114,79 +139,53 @@ protected:
    * A ThenValue tracks a single consumer waiting on the promise. When a consumer
    * invokes promise->Then(...), a ThenValue is created. Once the Promise is
    * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
    * invokes the resolve/reject method and then deletes the ThenValue.
    */
   class ThenValueBase : public Consumer
   {
   public:
-    class ResolveRunnable : public nsRunnable
+    class ResolveOrRejectRunnable : public nsRunnable
     {
     public:
-      ResolveRunnable(ThenValueBase* aThenValue, ResolveValueType aResolveValue)
+      ResolveOrRejectRunnable(ThenValueBase* aThenValue, ResolveOrRejectValue& aValue)
         : mThenValue(aThenValue)
-        , mResolveValue(aResolveValue) {}
+        , mValue(aValue) {}
 
-      ~ResolveRunnable()
+      ~ResolveOrRejectRunnable()
       {
         MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected());
       }
 
       NS_IMETHODIMP Run()
       {
-        PROMISE_LOG("ResolveRunnable::Run() [this=%p]", this);
-        mThenValue->DoResolve(mResolveValue);
+        PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
+        mThenValue->DoResolveOrReject(mValue);
         mThenValue = nullptr;
         return NS_OK;
       }
 
     private:
       nsRefPtr<ThenValueBase> mThenValue;
-      ResolveValueType mResolveValue;
-    };
-
-    class RejectRunnable : public nsRunnable
-    {
-    public:
-      RejectRunnable(ThenValueBase* aThenValue, RejectValueType aRejectValue)
-        : mThenValue(aThenValue)
-        , mRejectValue(aRejectValue) {}
-
-      ~RejectRunnable()
-      {
-        MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected());
-      }
-
-      NS_IMETHODIMP Run()
-      {
-        PROMISE_LOG("RejectRunnable::Run() [this=%p]", this);
-        mThenValue->DoReject(mRejectValue);
-        mThenValue = nullptr;
-        return NS_OK;
-      }
-
-    private:
-      nsRefPtr<ThenValueBase> mThenValue;
-      RejectValueType mRejectValue;
+      ResolveOrRejectValue mValue;
     };
 
     explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
       : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
 
     void Dispatch(MediaPromise *aPromise)
     {
       aPromise->mMutex.AssertCurrentThreadOwns();
       MOZ_ASSERT(!aPromise->IsPending());
-      bool resolved = aPromise->mResolveValue.isSome();
+
       nsRefPtr<nsRunnable> runnable =
-        resolved ? static_cast<nsRunnable*>(new (typename ThenValueBase::ResolveRunnable)(this, aPromise->mResolveValue.ref()))
-                 : static_cast<nsRunnable*>(new (typename ThenValueBase::RejectRunnable)(this, aPromise->mRejectValue.ref()));
+        static_cast<nsRunnable*>(new (typename ThenValueBase::ResolveOrRejectRunnable)(this, aPromise->mValue));
       PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
-                  resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
+                  aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
                   runnable.get(), aPromise, this);
 
       // Promise consumers are allowed to disconnect the Consumer object and
       // then shut down the thread or task queue that the promise result would
       // be dispatched on. So we unfortunately can't assert that promise
       // dispatch succeeds. :-(
       mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess);
     }
@@ -194,18 +193,17 @@ protected:
     virtual void Disconnect() override
     {
       MOZ_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
       MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete);
       Consumer::mDisconnected = true;
     }
 
   protected:
-    virtual void DoResolve(ResolveValueType aResolveValue) = 0;
-    virtual void DoReject(RejectValueType aRejectValue) = 0;
+    virtual void DoResolveOrReject(ResolveOrRejectValue& aValue) = 0;
 
     nsRefPtr<AbstractThread> mResponseTarget; // May be released on any thread.
     const char* mCallSite;
   };
 
   /*
    * We create two overloads for invoking Resolve/Reject Methods so as to
    * make the resolve/reject value argument "optional".
@@ -250,42 +248,30 @@ protected:
 
     // If a Consumer has been disconnected, we don't guarantee that the
     // resolve/reject runnable will be dispatched. Null out our refcounted
     // this-value now so that it's released predictably on the dispatch thread.
     mThisVal = nullptr;
   }
 
   protected:
-    virtual void DoResolve(ResolveValueType aResolveValue) override
+    virtual void DoResolveOrReject(ResolveOrRejectValue& aValue) override
     {
       Consumer::mComplete = true;
       if (Consumer::mDisconnected) {
         MOZ_ASSERT(!mThisVal);
-        PROMISE_LOG("ThenValue::DoResolve disconnected - bailing out [this=%p]", this);
+        PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
         return;
       }
-      InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aResolveValue);
 
-      // Null out mThisVal after invoking the callback so that any references are
-      // released predictably on the dispatch thread. Otherwise, it would be
-      // released on whatever thread last drops its reference to the ThenValue,
-      // which may or may not be ok.
-      mThisVal = nullptr;
-    }
-
-    virtual void DoReject(RejectValueType aRejectValue) override
-    {
-      Consumer::mComplete = true;
-      if (Consumer::mDisconnected) {
-        MOZ_ASSERT(!mThisVal);
-        PROMISE_LOG("ThenValue::DoReject disconnected - bailing out [this=%p]", this);
-        return;
+      if (aValue.IsResolve()) {
+        InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
+      } else {
+        InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
       }
-      InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aRejectValue);
 
       // Null out mThisVal after invoking the callback so that any references are
       // released predictably on the dispatch thread. Otherwise, it would be
       // released on whatever thread last drops its reference to the ThenValue,
       // which may or may not be ok.
       mThisVal = nullptr;
     }
 
@@ -337,17 +323,17 @@ public:
     if (!IsPending()) {
       ForwardTo(chainedPromise);
     } else {
       mChainedPromises.AppendElement(chainedPromise);
     }
   }
 
 protected:
-  bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
+  bool IsPending() { return mValue.IsNothing(); }
   void DispatchAll()
   {
     mMutex.AssertCurrentThreadOwns();
     for (size_t i = 0; i < mThenValues.Length(); ++i) {
       mThenValues[i]->Dispatch(this);
     }
     mThenValues.Clear();
 
@@ -355,35 +341,34 @@ protected:
       ForwardTo(mChainedPromises[i]);
     }
     mChainedPromises.Clear();
   }
 
   void ForwardTo(Private* aOther)
   {
     MOZ_ASSERT(!IsPending());
-    if (mResolveValue.isSome()) {
-      aOther->Resolve(mResolveValue.ref(), "<chained promise>");
+    if (mValue.IsResolve()) {
+      aOther->Resolve(mValue.ResolveValue(), "<chained promise>");
     } else {
-      aOther->Reject(mRejectValue.ref(), "<chained promise>");
+      aOther->Reject(mValue.RejectValue(), "<chained promise>");
     }
   }
 
   ~MediaPromise()
   {
     PROMISE_LOG("MediaPromise::~MediaPromise [this=%p]", this);
     MOZ_ASSERT(!IsPending());
     MOZ_ASSERT(mThenValues.IsEmpty());
     MOZ_ASSERT(mChainedPromises.IsEmpty());
   };
 
   const char* mCreationSite; // For logging
   Mutex mMutex;
-  Maybe<ResolveValueType> mResolveValue;
-  Maybe<RejectValueType> mRejectValue;
+  ResolveOrRejectValue mValue;
   nsTArray<nsRefPtr<ThenValueBase>> mThenValues;
   nsTArray<nsRefPtr<Private>> mChainedPromises;
   bool mHaveConsumer;
 };
 
 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 class MediaPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
   : public MediaPromise<ResolveValueT, RejectValueT, IsExclusive>
@@ -391,26 +376,26 @@ class MediaPromise<ResolveValueT, Reject
 public:
   explicit Private(const char* aCreationSite) : MediaPromise(aCreationSite) {}
 
   void Resolve(ResolveValueT aResolveValue, const char* aResolveSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(IsPending());
     PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
-    mResolveValue.emplace(aResolveValue);
+    mValue.SetResolve(aResolveValue);
     DispatchAll();
   }
 
   void Reject(RejectValueT aRejectValue, const char* aRejectSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(IsPending());
     PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
-    mRejectValue.emplace(aRejectValue);
+    mValue.SetReject(aRejectValue);
     DispatchAll();
   }
 };
 
 /*
  * Class to encapsulate a promise for a particular role. Use this as the member
  * variable for a class whose method returns a promise.
  */