Bug 1390739 - Dispatch the task to main thread if the callback of CDM does not on main thread. r=cpearce, a=lizzard
authorJames Cheng <jacheng@mozilla.com>
Wed, 16 Aug 2017 13:36:08 +0800
changeset 423734 7b56dc8cc01b9d711a011a439dcc5ff48410ab8e
parent 423733 553d2a5f53a3da40f55babcd83d239939e0dda5b
child 423735 c74cd9410e332fb20ebf2f7baab26f1d55ce2fe0
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, lizzard
bugs1390739
milestone56.0
Bug 1390739 - Dispatch the task to main thread if the callback of CDM does not on main thread. r=cpearce, a=lizzard MozReview-Commit-ID: E5sjEKR4bRQ
dom/media/gmp/ChromiumCDMChild.cpp
dom/media/gmp/ChromiumCDMChild.h
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -184,94 +184,139 @@ ChromiumCDMChild::SetTimer(int64_t aDela
 
 cdm::Time
 ChromiumCDMChild::GetCurrentWallTime()
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   return base::Time::Now().ToDoubleT();
 }
 
+template <typename MethodType, typename... ParamType>
 void
-ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
-                                             const char* aSessionId,
-                                             uint32_t aSessionIdSize)
+ChromiumCDMChild::CallMethod(MethodType aMethod, ParamType&&... aParams)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
-  GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
-          ", sid=%s)",
-          aPromiseId,
-          aSessionId);
+  // Avoid calling member function after destroy.
+  if (!mDestroyed) {
+    Unused << (this->*aMethod)(Forward<ParamType>(aParams)...);
+  }
+}
 
+template<typename MethodType, typename... ParamType>
+void
+ChromiumCDMChild::CallOnMessageLoopThread(const char* const aName,
+                                          MethodType aMethod,
+                                          ParamType&&... aParams)
+{
+  if (IsOnMessageLoopThread()) {
+    CallMethod(aMethod, Forward<ParamType>(aParams)...);
+  } else {
+    auto m = &ChromiumCDMChild::CallMethod<
+        decltype(aMethod), const typename RemoveReference<ParamType>::Type&...>;
+    RefPtr<mozilla::Runnable> t =
+      NewRunnableMethod<decltype(aMethod),
+                        const typename RemoveReference<ParamType>::Type...>(
+                        aName,
+                        this,
+                        m,
+                        aMethod,
+                        Forward<ParamType>(aParams)...);
+    mPlugin->GMPMessageLoop()->PostTask(t.forget());
+  }
+}
+
+bool
+ChromiumCDMChild::OnResolveNewSessionPromiseInternal(uint32_t aPromiseId,
+                                                     const nsCString& aSessionId)
+{
+  MOZ_ASSERT(IsOnMessageLoopThread());
   if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
     // As laid out in the Chromium CDM API, if the CDM fails to load
     // a session it calls OnResolveNewSessionPromise with nullptr as the sessionId.
     // We can safely assume this means that we have failed to load a session
     // as the other methods specify calling 'OnRejectPromise' when they fail.
     bool loadSuccessful = aSessionId != nullptr;
     GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
             "resolving %s load session ",
             aPromiseId,
-            aSessionId,
+            aSessionId.get(),
             (loadSuccessful ? "successful" : "failed"));
-    Unused << SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
     mLoadSessionPromiseIds.RemoveElement(aPromiseId);
-    return;
+    return SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
   }
 
-  Unused << SendOnResolveNewSessionPromise(aPromiseId,
-                                           nsCString(aSessionId, aSessionIdSize));
+  return SendOnResolveNewSessionPromise(aPromiseId,
+                                        aSessionId);
+}
+void
+ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
+                                             const char* aSessionId,
+                                             uint32_t aSessionIdSize)
+{
+  GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
+          ", sid=%s)",
+          aPromiseId,
+          aSessionId);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveNewSessionPromise",
+                          &ChromiumCDMChild::OnResolveNewSessionPromiseInternal,
+                          aPromiseId,
+                          nsCString(aSessionId, aSessionIdSize));
+
 }
 
 void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
-  Unused << SendOnResolvePromise(aPromiseId);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolvePromise",
+                          &ChromiumCDMChild::SendOnResolvePromise,
+                          aPromiseId);
 }
 
 void
 ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
                                   cdm::Error aError,
                                   uint32_t aSystemCode,
                                   const char* aErrorMessage,
                                   uint32_t aErrorMessageSize)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32 ", err=%" PRIu32
           " code=%" PRIu32 ", msg='%s')",
           aPromiseId,
           aError,
           aSystemCode,
           aErrorMessage);
-  Unused << SendOnRejectPromise(aPromiseId,
-                                static_cast<uint32_t>(aError),
-                                aSystemCode,
-                                nsCString(aErrorMessage, aErrorMessageSize));
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnRejectPromise",
+                          &ChromiumCDMChild::SendOnRejectPromise,
+                          aPromiseId,
+                          static_cast<uint32_t>(aError),
+                          aSystemCode,
+                          nsCString(aErrorMessage, aErrorMessageSize));
 }
 
 void
 ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
                                    uint32_t aSessionIdSize,
                                    cdm::MessageType aMessageType,
                                    const char* aMessage,
                                    uint32_t aMessageSize,
                                    const char* aLegacyDestinationUrl,
                                    uint32_t aLegacyDestinationUrlLength)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
           " size=%" PRIu32 ")",
           aSessionId,
           aMessageType,
           aMessageSize);
   nsTArray<uint8_t> message;
   message.AppendElements(aMessage, aMessageSize);
-  Unused << SendOnSessionMessage(nsCString(aSessionId, aSessionIdSize),
-                                 static_cast<uint32_t>(aMessageType),
-                                 message);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
+                          &ChromiumCDMChild::SendOnSessionMessage,
+                          nsCString(aSessionId, aSessionIdSize),
+                          static_cast<uint32_t>(aMessageType),
+                          message);
 }
 
 static nsCString
 ToString(const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount)
 {
   nsCString str;
   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     if (!str.IsEmpty()) {
@@ -287,74 +332,78 @@ ToString(const cdm::KeyInformation* aKey
 
 void
 ChromiumCDMChild::OnSessionKeysChange(const char *aSessionId,
                                       uint32_t aSessionIdSize,
                                       bool aHasAdditionalUsableKey,
                                       const cdm::KeyInformation* aKeysInfo,
                                       uint32_t aKeysInfoCount)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
           aSessionId,
           ToString(aKeysInfo, aKeysInfoCount).get());
 
   nsTArray<CDMKeyInformation> keys;
   keys.SetCapacity(aKeysInfoCount);
   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     const cdm::KeyInformation& key = aKeysInfo[i];
     nsTArray<uint8_t> kid;
     kid.AppendElements(key.key_id, key.key_id_size);
     keys.AppendElement(CDMKeyInformation(kid, key.status, key.system_code));
   }
-  Unused << SendOnSessionKeysChange(nsCString(aSessionId, aSessionIdSize),
-                                    keys);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
+                          &ChromiumCDMChild::SendOnSessionKeysChange,
+                          nsCString(aSessionId, aSessionIdSize),
+                          keys);
+
 }
 
 void
 ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
                                      uint32_t aSessionIdSize,
                                      cdm::Time aNewExpiryTime)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
           aSessionId,
           aNewExpiryTime);
-  Unused << SendOnExpirationChange(nsCString(aSessionId, aSessionIdSize),
-                                   aNewExpiryTime);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnExpirationChange",
+                          &ChromiumCDMChild::SendOnExpirationChange,
+                          nsCString(aSessionId, aSessionIdSize),
+                          aNewExpiryTime);
 }
 
 void
 ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
                                   uint32_t aSessionIdSize)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
-  Unused << SendOnSessionClosed(nsCString(aSessionId, aSessionIdSize));
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionClosed",
+                          &ChromiumCDMChild::SendOnSessionClosed,
+                          nsCString(aSessionId, aSessionIdSize));
 }
 
 void
 ChromiumCDMChild::OnLegacySessionError(const char* aSessionId,
                                        uint32_t aSessionIdLength,
                                        cdm::Error aError,
                                        uint32_t aSystemCode,
                                        const char* aErrorMessage,
                                        uint32_t aErrorMessageLength)
 {
-  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnLegacySessionError(sid=%s, error=%" PRIu32
           " msg='%s')",
           aSessionId,
           aError,
           aErrorMessage);
-  Unused << SendOnLegacySessionError(
-    nsCString(aSessionId, aSessionIdLength),
-    static_cast<uint32_t>(aError),
-    aSystemCode,
-    nsCString(aErrorMessage, aErrorMessageLength));
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnLegacySessionError",
+                          &ChromiumCDMChild::SendOnLegacySessionError,
+                          nsCString(aSessionId, aSessionIdLength),
+                          static_cast<uint32_t>(aError),
+                          aSystemCode,
+                          nsCString(aErrorMessage, aErrorMessageLength));
 }
 
 cdm::FileIO*
 ChromiumCDMChild::CreateFileIO(cdm::FileIOClient * aClient)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::CreateFileIO()");
   if (!mPersistentStateAllowed) {
@@ -795,16 +844,17 @@ ChromiumCDMChild::RecvDestroy()
   GMP_LOG("ChromiumCDMChild::RecvDestroy()");
 
   MOZ_ASSERT(!mDecoderInitialized);
 
   if (mCDM) {
     mCDM->Destroy();
     mCDM = nullptr;
   }
+  mDestroyed = true;
 
   Unused << Send__delete__(this);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvGiveBuffer(ipc::Shmem&& aBuffer)
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -74,16 +74,19 @@ public:
                                     cdm::Status aDecoderStatus) override {}
   cdm::FileIO* CreateFileIO(cdm::FileIOClient* aClient) override;
 
   void GiveBuffer(ipc::Shmem&& aBuffer);
 
 protected:
   ~ChromiumCDMChild();
 
+  bool OnResolveNewSessionPromiseInternal(uint32_t aPromiseId,
+                                          const nsCString& aSessionId);
+
   bool IsOnMessageLoopThread();
 
   ipc::IPCResult RecvGiveBuffer(ipc::Shmem&& aShmem) override;
   ipc::IPCResult RecvPurgeShmems() override;
   void PurgeShmems();
   ipc::IPCResult RecvInit(const bool& aAllowDistinctiveIdentifier,
                           const bool& aAllowPersistentState) override;
   ipc::IPCResult RecvSetServerCertificate(
@@ -113,26 +116,33 @@ protected:
   ipc::IPCResult RecvDecryptAndDecodeFrame(
     const CDMInputBuffer& aBuffer) override;
   ipc::IPCResult RecvDrain() override;
   ipc::IPCResult RecvDestroy() override;
 
   void ReturnOutput(WidevineVideoFrame& aFrame);
   bool HasShmemOfSize(size_t aSize) const;
 
+  template <typename MethodType, typename... ParamType>
+  void CallMethod(MethodType, ParamType&&...);
+
+  template<typename MethodType, typename... ParamType>
+  void CallOnMessageLoopThread(const char* const, MethodType, ParamType&&...);
+
   GMPContentChild* mPlugin = nullptr;
   cdm::ContentDecryptionModule_8* mCDM = nullptr;
 
   typedef SimpleMap<uint64_t> DurationMap;
   DurationMap mFrameDurations;
   nsTArray<uint32_t> mLoadSessionPromiseIds;
 
   cdm::Size mCodedSize;
   nsTArray<ipc::Shmem> mBuffers;
 
   bool mDecoderInitialized = false;
   bool mPersistentStateAllowed = false;
+  bool mDestroyed = false;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // ChromiumCDMChild_h_